Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2798 lines
105 KiB

  1. // 32BitDibWrapper.cpp: implementation of the C32BitDibWrapper class.
  2. //
  3. //////////////////////////////////////////////////////////////////////
  4. #include "precomp.h"
  5. #pragma hdrstop
  6. #include "32BitDib.h"
  7. // helper functions
  8. // sum of RGB vals
  9. inline ULONG Intensity(ULONG value)
  10. {
  11. return(value&0xff)+((value&0xff00)>>8)+((value&0xff0000)>>16);
  12. }
  13. // we should make a Difference Template to clean up this code
  14. inline UCHAR Difference(UCHAR a, UCHAR b)
  15. {
  16. if (a>b) return(a-b);
  17. else return(b-a);
  18. }
  19. inline ULONG Difference(ULONG a, ULONG b)
  20. {
  21. if (a>b) return(a-b);
  22. else return(b-a);
  23. }
  24. inline LONG Difference(LONG a, LONG b)
  25. {
  26. if (a>b) return(a-b);
  27. else return(b-a);
  28. }
  29. int inline MAX(int a, int b)
  30. {
  31. if (a>b) return(a);
  32. return(b);
  33. }
  34. int inline MIN(int a, int b)
  35. {
  36. if (a<b) return(a);
  37. return(b);
  38. }
  39. // shadows are gray... if you aint gray... you aint a shadow
  40. // this function hasn't yet been optimized
  41. inline ULONG DifferenceFromGray(ULONG value)
  42. {
  43. UCHAR g,b;//,r;
  44. // r=(UCHAR)(value& 0x0000ff);
  45. g=(UCHAR)((value& 0x00ff00)>>8);
  46. b=(UCHAR)((value& 0xff0000)>>16);
  47. // use this instead of the complete formula (uncomment the commented out code for the complete formula)
  48. // allow yellow scanner backgrounds
  49. return(ULONG)(Difference(b,g));//+Difference(r,g)+Difference(g,b));
  50. }
  51. // sets up a C32BitDibWrapper where each pixel (x,y) is the difference bettween the value of the pixel (x,y) on
  52. // bitmap1 and the pixel (x,y) on bitmap2
  53. int C32BitDibWrapper::CreateDifferenceBitmap(C32BitDibWrapper *pBitmap1, C32BitDibWrapper *pBitmap2) // constructs a new dib that is the difference of the two other dibs
  54. { // image - blur(image) = detect edges.
  55. //
  56. // Destroy the old bitmap
  57. //
  58. if (m_pBits)
  59. {
  60. delete[] m_pBits;
  61. m_pBits = NULL;
  62. }
  63. m_nBitmapWidth=-1;
  64. m_nBitmapHeight=-1;
  65. //
  66. // Validate arguments
  67. //
  68. if (pBitmap1==NULL || pBitmap2==NULL)
  69. {
  70. return(FALSE);
  71. }
  72. if (pBitmap1->m_nBitmapWidth != pBitmap2->m_nBitmapWidth)
  73. {
  74. return(FALSE);
  75. }
  76. if (pBitmap1->m_nBitmapHeight != pBitmap2->m_nBitmapHeight)
  77. {
  78. return(FALSE);
  79. }
  80. if (pBitmap1->m_pBits==NULL || pBitmap2->m_pBits==NULL)
  81. {
  82. return(FALSE);
  83. }
  84. //
  85. // How many bytes do we need?
  86. //
  87. int nNumBytes = pBitmap1->m_nBitmapWidth * pBitmap1->m_nBitmapHeight * sizeof(ULONG);
  88. //
  89. // Allocate the bytes, return false if we weren't successful
  90. //
  91. m_pBits = new BYTE[nNumBytes];
  92. if (m_pBits==NULL)
  93. {
  94. return(FALSE);
  95. }
  96. //
  97. // Save the dimensions
  98. //
  99. m_nBitmapWidth=pBitmap1->m_nBitmapWidth;
  100. m_nBitmapHeight=pBitmap1->m_nBitmapHeight;
  101. //
  102. // Compute the difference
  103. //
  104. for (int i=0;i<nNumBytes;i++)
  105. {
  106. m_pBits[i]=Difference(pBitmap1->m_pBits[i],pBitmap2->m_pBits[i]);
  107. }
  108. return(TRUE);
  109. }
  110. // creates a C32BitDibWrapper which is identical to the C32BitDibWrapper passed as *bitmap
  111. C32BitDibWrapper::C32BitDibWrapper(C32BitDibWrapper *pBitmap) // copy constructor
  112. : m_pBits(NULL),
  113. m_nBitmapWidth(-1),
  114. m_nBitmapHeight(-1)
  115. {
  116. if (pBitmap && pBitmap->IsValid())
  117. {
  118. int nNumWords=pBitmap->m_nBitmapWidth*pBitmap->m_nBitmapHeight;
  119. ULONG* pBitmapCopy = new ULONG[nNumWords];
  120. ULONG* pSourceBitmap = (ULONG*)pBitmap->m_pBits;
  121. if (pBitmapCopy && pSourceBitmap)
  122. {
  123. CopyMemory( pBitmapCopy, pSourceBitmap, nNumWords*sizeof(ULONG) );
  124. m_pBits=(BYTE *)pBitmapCopy;
  125. m_nBitmapHeight=pBitmap->m_nBitmapHeight;
  126. m_nBitmapWidth=pBitmap->m_nBitmapWidth;
  127. }
  128. }
  129. }
  130. // creates a blank dib wrapper w pixels wide and h pixels high
  131. C32BitDibWrapper::C32BitDibWrapper(int w, int h)
  132. : m_pBits(NULL),
  133. m_nBitmapWidth(-1),
  134. m_nBitmapHeight(-1)
  135. {
  136. int nNumWords=w*h;
  137. ULONG *pBitmapCopy = new ULONG[nNumWords];
  138. if (pBitmapCopy)
  139. {
  140. ZeroMemory(pBitmapCopy,nNumWords*sizeof(ULONG));
  141. m_pBits=(BYTE*)pBitmapCopy;
  142. m_nBitmapHeight=h;
  143. m_nBitmapWidth=w;
  144. }
  145. }
  146. // creates a C32BitDibWrapper given the bitmap refered to by pBitmap
  147. C32BitDibWrapper::C32BitDibWrapper(BITMAP bm)
  148. : m_pBits(NULL),
  149. m_nBitmapWidth(-1),
  150. m_nBitmapHeight(-1)
  151. {
  152. BYTE* pDibBits=(BYTE*)(bm.bmBits);
  153. if (pDibBits!=NULL && bm.bmWidth>0 && bm.bmHeight>0 && bm.bmBitsPixel>0 && bm.bmBitsPixel<=32) // is it a valid bitmap?
  154. {
  155. int nDepth = bm.bmBitsPixel;
  156. m_nBitmapWidth = bm.bmWidth;
  157. m_nBitmapHeight = bm.bmHeight;
  158. //
  159. // convert to a 32 bit bitmap
  160. //
  161. m_pBits=ConvertBitmap(pDibBits,nDepth,32);
  162. if (!m_pBits)
  163. {
  164. m_nBitmapWidth=-1;
  165. m_nBitmapHeight=-1;
  166. }
  167. }
  168. }
  169. // constructor from a memory mapped file bitmap
  170. C32BitDibWrapper::C32BitDibWrapper(BYTE* pDib)
  171. : m_pBits(NULL),
  172. m_nBitmapWidth(-1),
  173. m_nBitmapHeight(-1)
  174. {
  175. if (pDib)
  176. {
  177. //
  178. // get pointer to just the image bits:
  179. //
  180. PBITMAPINFO pBitmapInfo=(PBITMAPINFO)(pDib + sizeof(BITMAPFILEHEADER));
  181. BYTE* pDibBits = NULL;
  182. switch (pBitmapInfo->bmiHeader.biBitCount)
  183. {
  184. case 24:
  185. pDibBits=pDib+GetBmiSize((PBITMAPINFO)(pDib + sizeof(BITMAPFILEHEADER)))+ sizeof(BITMAPFILEHEADER);
  186. break;
  187. case 8:
  188. pDibBits=pDib+GetBmiSize((PBITMAPINFO)(pDib + sizeof(BITMAPFILEHEADER)))+ sizeof(BITMAPFILEHEADER)-256*4+4;
  189. break;
  190. case 1:
  191. pDibBits=pDib+GetBmiSize((PBITMAPINFO)(pDib + sizeof(BITMAPFILEHEADER)))+ sizeof(BITMAPFILEHEADER)-4;
  192. break;
  193. }
  194. if (pDibBits)
  195. {
  196. m_pBits=ConvertBitmap(pDibBits,pBitmapInfo->bmiHeader.biBitCount,32);// convert to a 32 bit bitmap
  197. if (m_pBits)
  198. {
  199. m_nBitmapWidth=pBitmapInfo->bmiHeader.biWidth;
  200. m_nBitmapHeight=pBitmapInfo->bmiHeader.biHeight;
  201. }
  202. }
  203. }
  204. }
  205. // create an empty wrapper
  206. // we later expect to fill the wrapper using CreateDifferenceBitmap
  207. C32BitDibWrapper::C32BitDibWrapper(void)
  208. : m_pBits(NULL),
  209. m_nBitmapWidth(-1),
  210. m_nBitmapHeight(-1)
  211. {
  212. }
  213. C32BitDibWrapper::~C32BitDibWrapper(void)
  214. {
  215. Destroy();
  216. }
  217. void C32BitDibWrapper::Destroy(void)
  218. {
  219. if (m_pBits)
  220. {
  221. delete[] m_pBits;
  222. m_pBits = NULL;
  223. }
  224. m_nBitmapWidth=-1;
  225. m_nBitmapHeight=-1;
  226. }
  227. //
  228. // helper function which converts between 32 bit and other less worthy formats
  229. // 32 bit dibs are stored in the following format
  230. // xxxxxxxxRRRRRRRRGGGGGGGGBBBBBBBB 8 blank bits followed by 8 bits for each RGB channel
  231. //
  232. // not optimized for speed
  233. //
  234. // if we are being handed a large number of 300 dpi bitmaps, this could become an important function to
  235. // optimize... otherwise its fine in its current form
  236. //
  237. BYTE* C32BitDibWrapper::ConvertBitmap( BYTE* pSource, int bitsPerSource, int bitsPerDest )
  238. {
  239. BYTE* pDest = NULL;
  240. long x, y, nSourceLocation=0, nTargetLocation=0;
  241. int i, nDWordAlign;
  242. //
  243. // Compute the dword align space for each line
  244. //
  245. if (m_nBitmapWidth%4!=0)
  246. nDWordAlign=4-(m_nBitmapWidth*3)%4;
  247. else nDWordAlign=0;
  248. //
  249. // Convert from a 24 bit bitmap to a 32 bit bitmap
  250. // Pretty straight forward except that we have to watch out for
  251. // DWORD align stuff with 24 bit bitmaps
  252. //
  253. if (bitsPerSource==24 && bitsPerDest==32)
  254. {
  255. pDest = new BYTE[m_nBitmapWidth*m_nBitmapHeight*sizeof(ULONG)];
  256. //
  257. // with fancy bit twiddling, we can get things done with one operation per 32
  258. // bit pixel instead of 3 without much trouble if this routine becomes a
  259. // performance bottlekneck, we should modify this code
  260. //
  261. // loop through all pixels adding 8 bits of zeroed out data at the end of
  262. // each pSource line. 00000000RRRRRRRRGGGGGGGGBBBBBBBB
  263. //
  264. if (pDest)
  265. {
  266. for (y=0;y<m_nBitmapHeight;y++)
  267. {
  268. for (x=0;x<m_nBitmapWidth;x++)
  269. {
  270. pDest[nTargetLocation++]=pSource[nSourceLocation++];
  271. pDest[nTargetLocation++]=pSource[nSourceLocation++];
  272. pDest[nTargetLocation++]=pSource[nSourceLocation++];
  273. pDest[nTargetLocation++]=0;
  274. }
  275. nSourceLocation+=nDWordAlign; // skip nDWordAlign pixels... 24 bit bitmaps are DWORD alligned
  276. }
  277. }
  278. return(pDest);
  279. }
  280. //
  281. // Convert from an 8 bit bitmap to a 32 bit bitmap
  282. //
  283. else if (bitsPerSource==8 && bitsPerDest==32)
  284. {
  285. pDest = new BYTE[m_nBitmapWidth*m_nBitmapHeight*sizeof(ULONG)];
  286. if (pDest)
  287. {
  288. for (y=0;y<m_nBitmapHeight;y++) // loop through all pixels (x,y)
  289. {
  290. for (x=0;x<m_nBitmapWidth;x++)
  291. {
  292. pDest[nTargetLocation++]=pSource[nSourceLocation];
  293. pDest[nTargetLocation++]=pSource[nSourceLocation];
  294. pDest[nTargetLocation++]=pSource[nSourceLocation];
  295. pDest[nTargetLocation++]=0;
  296. nSourceLocation++;
  297. }
  298. if (m_nBitmapWidth%4!=0)
  299. {
  300. //
  301. // handle dword alignment issues for 8 bit dibs
  302. //
  303. nSourceLocation+=4-(m_nBitmapWidth)%4;
  304. }
  305. }
  306. }
  307. return(pDest);
  308. }
  309. //
  310. // Convert from a 1 bit B&W bitmap to a 32 bit bitmap
  311. //
  312. if (bitsPerSource==1 && bitsPerDest==32)
  313. {
  314. const int c_nConvertSize = 256;
  315. const int c_nMaskSize = 8;
  316. BYTE mask[c_nMaskSize];
  317. BYTE convert[c_nConvertSize];
  318. BYTE nCurrent;
  319. int nByte = 0,nBit = 0;
  320. int nLineWidth;
  321. //
  322. // mask[i] = 2^i
  323. //
  324. for (i=0;i<c_nMaskSize;i++)
  325. {
  326. mask[i]=1<<i;
  327. }
  328. //
  329. // all values of convert other than convert[0] are set to 0
  330. //
  331. convert[0]=0;
  332. for (i=1;i<c_nConvertSize;i++)
  333. {
  334. convert[i]=255;
  335. }
  336. nLineWidth=((m_nBitmapWidth+31)/32)*4;
  337. //
  338. // loop through all bitmap pixels keeping track of
  339. // byte which indicates the byte position of the pixel (x,y) in the pSource bitmap
  340. // bit which indicates the bit position of the pixel (x,y) within the byte
  341. // desLocation which represents the byte position of the 32 bit dib wrapper
  342. //
  343. // loop through all bitmap pixels keeping track of byte which indicates the
  344. // byte position of the pixel (x,y) in the pSource bitmap bit which indicates
  345. // the bit position of the pixel (x,y) within the byte desLocation which
  346. // represents the byte position of the 32 bit dib wrapper
  347. //
  348. pDest = new BYTE[m_nBitmapWidth*m_nBitmapHeight*sizeof(ULONG)];
  349. if (pDest)
  350. {
  351. for (y=0;y<m_nBitmapHeight;y++)
  352. {
  353. nBit=0;
  354. nByte=y*nLineWidth;
  355. for (x=0;x<m_nBitmapWidth;x++)
  356. {
  357. if (nBit==8)
  358. {
  359. nBit=0;
  360. nByte++;
  361. }
  362. nCurrent=pSource[nByte]&mask[nBit];
  363. nCurrent=convert[nCurrent];
  364. pDest[nTargetLocation++]=static_cast<BYTE>(nCurrent);
  365. pDest[nTargetLocation++]=static_cast<BYTE>(nCurrent);
  366. //
  367. // hack to prevent shadow detection for 1 nBit dibs.
  368. // set the blue channel to 150 so that shadow detection doesn't kick in
  369. //
  370. pDest[nTargetLocation++]=nCurrent&150;
  371. pDest[nTargetLocation++]=0;
  372. nBit++;
  373. }
  374. }
  375. }
  376. return(pDest);
  377. }
  378. //
  379. // Only used for debugging purposes
  380. // Converts a 32 bit bitmap down to 24 bits so that we can quickly display it
  381. //
  382. if (bitsPerSource==32 && bitsPerDest==24) // pretty straight forward except that we have to watch out for DWORD align stuff with 24 bit bitmaps
  383. {
  384. pDest = new BYTE[(m_nBitmapWidth*3+nDWordAlign)*m_nBitmapHeight];
  385. if (pDest)
  386. {
  387. for (y=0;y<m_nBitmapHeight;y++)
  388. {
  389. for (x=0;x<m_nBitmapWidth;x++)
  390. {
  391. pDest[nTargetLocation++]=pSource[nSourceLocation++];
  392. pDest[nTargetLocation++]=pSource[nSourceLocation++];
  393. pDest[nTargetLocation++]=pSource[nSourceLocation++];
  394. //
  395. // pSource is 32 bits... ignore the first 8 bits
  396. //
  397. nSourceLocation++;
  398. }
  399. //
  400. // handle dword alignment issues for 24 bit dibs
  401. //
  402. for (i=0;i<nDWordAlign;i++)
  403. {
  404. pDest[nTargetLocation++]=255;
  405. }
  406. }
  407. }
  408. return(pDest);
  409. }
  410. return(pDest);
  411. }
  412. // blurs the bitmap both horizontally and vertically
  413. int C32BitDibWrapper::Blur(void)
  414. {
  415. BYTE *pBits=pointerToBlur();
  416. if (m_pBits)
  417. {
  418. delete[] m_pBits;
  419. }
  420. m_pBits = pBits;
  421. return(TRUE);
  422. }
  423. // this function should only be used if the current dib wrapper is blank
  424. int C32BitDibWrapper::CreateBlurBitmap(C32BitDibWrapper * pSource)
  425. {
  426. if (pSource!=NULL && pSource->m_pBits!=NULL)
  427. {
  428. Destroy();
  429. m_pBits=pSource->pointerToBlur();
  430. //
  431. // the blurred bitmap will have the same dimensions as the pSource bitmap
  432. //
  433. m_nBitmapWidth=pSource->m_nBitmapWidth;
  434. m_nBitmapHeight=pSource->m_nBitmapHeight;
  435. return(TRUE);
  436. }
  437. else
  438. {
  439. return(FALSE);
  440. }
  441. }
  442. // identical to the previous function, except that we use a horizontal blur instead of blur
  443. int C32BitDibWrapper::CreateHorizontalBlurBitmap(C32BitDibWrapper * pSource)
  444. {
  445. if (pSource!=NULL && pSource->IsValid())
  446. {
  447. Destroy();
  448. m_pBits=pSource->pointerToHorizontalBlur();
  449. if (m_pBits)
  450. {
  451. m_nBitmapWidth=pSource->m_nBitmapWidth;
  452. m_nBitmapHeight=pSource->m_nBitmapHeight;
  453. }
  454. return(TRUE);
  455. }
  456. else
  457. {
  458. return(FALSE);
  459. }
  460. }
  461. int C32BitDibWrapper::CreateVerticalBlurBitmap(C32BitDibWrapper * pSource)
  462. {
  463. //
  464. // Nuke the old bitmap
  465. //
  466. Destroy();
  467. if (pSource!=NULL && pSource->IsValid())
  468. {
  469. m_pBits=pSource->pointerToVerticalBlur();
  470. m_nBitmapWidth=pSource->m_nBitmapWidth;
  471. m_nBitmapHeight=pSource->m_nBitmapHeight;
  472. return(TRUE);
  473. }
  474. return(FALSE);
  475. }
  476. // blur the bitmap
  477. BYTE* C32BitDibWrapper::pointerToBlur(void)
  478. {
  479. if (m_pBits!=NULL)
  480. {
  481. int x,y;
  482. int position; // position in old bitmap
  483. ULONG* pBlurredBitmap;
  484. ULONG* pSource;
  485. int numPixels;
  486. numPixels=m_nBitmapWidth*m_nBitmapHeight; // calculate the total number of pixels in the bitmap
  487. pSource = (ULONG *)m_pBits; // we want to deal with data in 32 bit chunks
  488. pBlurredBitmap = new ULONG[numPixels]; // create an array to hold the blurred bitmap
  489. if (pBlurredBitmap==NULL) return(NULL); // unable to alloc memory
  490. // handle edge pixels
  491. // we do not blur edge pixels
  492. // if needed, edge pixels could be blurred here
  493. // blur top and bottom edge pixels
  494. for (x=0;x<m_nBitmapWidth;x++)
  495. {
  496. pBlurredBitmap[x] = pSource[x]; // top row
  497. pBlurredBitmap[numPixels-x-1] = pSource[numPixels-x-1]; // bottom row
  498. }
  499. // vertical sides
  500. for (position=m_nBitmapWidth;position+m_nBitmapWidth<numPixels;position+=m_nBitmapWidth)
  501. {
  502. pBlurredBitmap[position] = pSource[position]; // left edge
  503. pBlurredBitmap[position+m_nBitmapWidth-1] = pSource[position+m_nBitmapWidth-1]; // right edge
  504. }
  505. // now blur the bitmap
  506. // position indicates the location of the pixel (x,y) in the array
  507. position=m_nBitmapWidth-1;
  508. for (y=1;y<m_nBitmapHeight-1;y++) // loop through all pixels except for 1 pixel wide outside edge
  509. {
  510. position++;
  511. for (x=1;x<m_nBitmapWidth-1;x++)
  512. {
  513. position++;
  514. // we wish to take the average of the pixel directly below the pixel, the pixel directly below the pixel
  515. // the pixel directly to the right of the pixel and the pixel directly to the left of the pixel
  516. // we can do this 1 dword at a time and without any bit shifting by the following algorithm
  517. // problem. we cannot simply add the values of all four pixels and then divide by four
  518. // because or bit overflow from one RGB channel to another. to avoid this overflow, we start by
  519. // eliminating the two low order bits in each of the 3 color channels
  520. // we use the filter 0xfafafa to remove the 2 low order bits from each of the 3 color channels
  521. // e.g. RRRRRRRRGGGGGGGGBBBBBBBB goes to RRRRRR00GGGGGG00BBBBBB00
  522. // next shift each pixel over two bits so RRRRRR00GGGGGG00BBBBBB00 --> 00RRRRRR00GGGGGG00BBBBBB
  523. // note: we save 3 bit shifts by adding all four filtered pixels and then bitshifting which comes out to the same thing
  524. // we can now add the four pixel values without channels overflowing. the value we get is off by an error factor
  525. // because we eliminated the two lowest bits from each value
  526. // we compensate for this error factor by applying the filter 0x030303 which translates
  527. // RRRRRRRRGGGGGGGGBBBBBBBB to 000000RR000000GG000000BB
  528. // giving us the two lowest order bits for each pixel. we can then safely add the two lowest order
  529. // bits for each pixel. we then divide the result by 4 and add it
  530. // to the value we got by ignoring the two lowest order bits.
  531. pBlurredBitmap[position] =
  532. (((pSource[position-1]&16579836) + // 0xfafafa
  533. (pSource[position+1]&16579836) +
  534. (pSource[position-m_nBitmapWidth]&16579836) +
  535. (pSource[position+m_nBitmapWidth]&16579836))>>2)+
  536. // compensate for error factor:
  537. ((((pSource[position-1]&197379) + // 0x030303
  538. (pSource[position+1]&197379) +
  539. (pSource[position-m_nBitmapWidth]&197379) +
  540. (pSource[position+m_nBitmapWidth]&197379))>>2)&197379);
  541. }
  542. position++;
  543. }
  544. return(BYTE *)pBlurredBitmap;
  545. }
  546. else
  547. {
  548. return(NULL);
  549. }
  550. }
  551. //
  552. // identical to pointerToBlur bitmap except that we only use the pixel to
  553. // the left and the pixel to the right of the bitmap for detailed comments,
  554. // see pointerToBlur
  555. //
  556. BYTE* C32BitDibWrapper::pointerToHorizontalBlur(void)
  557. {
  558. if (m_pBits!=NULL)
  559. {
  560. int x,y;
  561. int position; // position in old bitmap
  562. ULONG* pBlurredBitmap;
  563. ULONG* pSource;
  564. int numPixels;
  565. numPixels=m_nBitmapWidth*m_nBitmapHeight;
  566. pSource = (ULONG *)m_pBits;
  567. pBlurredBitmap = new ULONG[numPixels];
  568. if (pBlurredBitmap == NULL) return(NULL);
  569. // handle edge pixels
  570. // for edge pixels we simply copy the pixel into the pSource to the destination
  571. for (x=0;x<m_nBitmapWidth;x++)
  572. {
  573. pBlurredBitmap[x] = pSource[x]; // top row
  574. pBlurredBitmap[numPixels-x-1] = pSource[numPixels-x-1]; // bottom row
  575. }
  576. // vertical sides
  577. for (position=m_nBitmapWidth;position+m_nBitmapWidth<numPixels;position+=m_nBitmapWidth)
  578. {
  579. pBlurredBitmap[position] = pSource[position]; // left edge
  580. pBlurredBitmap[position+m_nBitmapWidth-1] = pSource[position+m_nBitmapWidth-1]; // right edge
  581. }
  582. // now blur the bitmap
  583. position=m_nBitmapWidth-1;
  584. for (y=1;y<m_nBitmapHeight-1;y++) // for all pixels, pSource[position] is the pixel at (x,y)
  585. {
  586. position++;
  587. for (x=1;x<m_nBitmapWidth-1;x++)
  588. {
  589. position++;
  590. pBlurredBitmap[position] =
  591. (((pSource[position-1]&0xfefefe) +
  592. (pSource[position+1]&0xfefefe))>>1)+
  593. ((((pSource[position-1]&0x010101) +
  594. (pSource[position+1]&0x010101))>>1)&0x010101);
  595. }
  596. position++;
  597. }
  598. return(BYTE *)pBlurredBitmap;
  599. }
  600. else
  601. {
  602. return(NULL);
  603. }
  604. }
  605. // blur vertically
  606. // same exact method as pointerToHorizontalBlur
  607. // useful for detecting vertical edges, etc.
  608. BYTE* C32BitDibWrapper::pointerToVerticalBlur(void)
  609. {
  610. if (m_pBits)
  611. {
  612. int x,y;
  613. int position; // position in old bitmap
  614. ULONG* pBlurredBitmap;
  615. ULONG* pSource;
  616. int numPixels;
  617. numPixels=m_nBitmapWidth*m_nBitmapHeight;
  618. pSource = (ULONG *)m_pBits;
  619. pBlurredBitmap = new ULONG[numPixels];
  620. if (pBlurredBitmap == NULL) return(NULL);
  621. // handle edge pixels
  622. for (x=0;x<m_nBitmapWidth;x++)
  623. {
  624. pBlurredBitmap[x] = pSource[x]; // top row
  625. pBlurredBitmap[numPixels-x-1] = pSource[numPixels-x-1]; // bottom row
  626. }
  627. // vertical sides
  628. for (position=m_nBitmapWidth;position+m_nBitmapWidth<numPixels;position+=m_nBitmapWidth)
  629. {
  630. pBlurredBitmap[position] = pSource[position]; // left edge
  631. pBlurredBitmap[position+m_nBitmapWidth-1] = pSource[position+m_nBitmapWidth-1]; // right edge
  632. }
  633. // now blur the bitmap
  634. position=m_nBitmapWidth-1;
  635. for (y=1;y<m_nBitmapHeight-1;y++) // pSource[position] is the pixel at (x,y)
  636. {
  637. position++;
  638. for (x=1;x<m_nBitmapWidth-1;x++)
  639. {
  640. position++;
  641. pBlurredBitmap[position] =
  642. (((pSource[position-m_nBitmapWidth]&0xfefefe) +
  643. (pSource[position+m_nBitmapWidth]&0xfefefe))>>1)+
  644. ((((pSource[position-m_nBitmapWidth]&0x010101) +
  645. (pSource[position+m_nBitmapWidth]&0x010101))>>1)&0x010101);
  646. }
  647. position++;
  648. }
  649. return(BYTE *)pBlurredBitmap;
  650. }
  651. else
  652. {
  653. return(NULL);
  654. }
  655. }
  656. // cuts the intensity of each pixel in half
  657. // useful for certain graphics effects
  658. int C32BitDibWrapper::HalfIntensity(void)
  659. {
  660. if (m_pBits)
  661. {
  662. int numPixels;
  663. int i;
  664. ULONG* pBitmapPixels;
  665. pBitmapPixels=(ULONG*)m_pBits;
  666. numPixels=m_nBitmapWidth*m_nBitmapHeight;
  667. // loop through all pixels
  668. for (i=0;i<numPixels;i++)
  669. pBitmapPixels[i]=(pBitmapPixels[i]&0xfefefe)>>1; // half intensity by first eliminating the lowest order bit from each pixel and then shifting all pixels by 1
  670. return(TRUE);
  671. }
  672. else
  673. {
  674. return(FALSE);
  675. }
  676. }
  677. // used repeatedly if the user hands us a 300 dpi image, etc.
  678. // HalfSize compacts a h x w bitmap down to a h/2 x w/2 bitmap
  679. int C32BitDibWrapper::HalfSize(void)
  680. {
  681. if (m_pBits)
  682. {
  683. int x,y;
  684. ULONG position; // position in old bitmap
  685. ULONG halfposition; // position in half (1/4 area) sized bitmap
  686. int oldWidth,oldHeight;
  687. ULONG* pOldBitmap;
  688. ULONG* pNewBitmap;
  689. pOldBitmap=(ULONG *)m_pBits; // we speed things up by dealing with 32 bit chunks instead of 8 bit chunks
  690. pNewBitmap = new ULONG[(m_nBitmapWidth/2)*(m_nBitmapHeight/2)]; // create an array to store a bitmap 1/4th the size of the origional bitmap
  691. if (pNewBitmap == NULL) return(FALSE); // out of memory
  692. position=0;
  693. halfposition=0;
  694. // loop through pixels 2 pixels at a time in each direction
  695. // at all times we insure that pOldBitmap[position] is the pixel at (x,y)
  696. // and pNewBitmap[halfposition] is the pixel at (x/2,y/2)
  697. for (y=0;y<m_nBitmapHeight-1;y+=2)
  698. {
  699. position=m_nBitmapWidth*y;
  700. for (x=0;x<m_nBitmapWidth-1;x+=2)
  701. {
  702. pNewBitmap[halfposition] = // we use the same algorithm for finding the average of four pixel values as used in pointerToBlur
  703. // see pointerToBlur for a detailed explaination
  704. (((pOldBitmap[position]&16579836) +
  705. (pOldBitmap[position+1]&16579836) +
  706. (pOldBitmap[position+m_nBitmapWidth]&16579836) +
  707. (pOldBitmap[position+m_nBitmapWidth+1]&16579836))>>2)+
  708. ((((pOldBitmap[position]&197379) +
  709. (pOldBitmap[position+1]&197379) +
  710. (pOldBitmap[position+m_nBitmapWidth]&197379) +
  711. (pOldBitmap[position+m_nBitmapWidth+1]&197379))>>2)&197379);
  712. position+=2;
  713. halfposition++;
  714. }
  715. }
  716. delete[] m_pBits; // destroy the old bitmap array
  717. m_nBitmapWidth=m_nBitmapWidth/2;
  718. m_nBitmapHeight=m_nBitmapHeight/2;
  719. m_pBits=(BYTE *)pNewBitmap;
  720. return(TRUE);
  721. }
  722. else
  723. {
  724. return(FALSE);
  725. }
  726. }
  727. // this function destorys regions where the edgeBitmapPixels
  728. // edgeBitmap holds edge informat, start defines the maximum color value for a pixel to start a shadow elimination search
  729. // maxPixel defines the maximum edge value allowed for a shadow pixel
  730. // differenceFromGrey defines the maximum difference from grey for a shadow pixel
  731. // enhanceEdges deals with edge enhancement
  732. int C32BitDibWrapper::KillShadows(C32BitDibWrapper * edgeBitmap, ULONG start, ULONG maxPixel, ULONG differenceFromGrey, ULONG min_guaranteed_not_shadow, bool enhanceEdges)
  733. {
  734. if (IsValid() && edgeBitmap && edgeBitmap->m_pBits)
  735. {
  736. int x,y,position, searchPosition, newPosition;
  737. ULONG searchEdge;
  738. ULONG * pEdgePixels;
  739. ULONG * pBitmapPixels;
  740. ULONG maxEdge;
  741. int numPixels=m_nBitmapWidth*m_nBitmapHeight;
  742. int *pShadowStack = new int[MAXSTACK];
  743. if (!pShadowStack)
  744. {
  745. //
  746. // we probably ran out of memory. die gracefully.
  747. //
  748. return(FALSE);
  749. }
  750. int stackHeight = 0;
  751. // we first mark all the border pixels so we don't go off the edge
  752. // this is much faster than other ways of doing bounds checking
  753. pEdgePixels=(ULONG *)edgeBitmap->m_pBits;
  754. pBitmapPixels=(ULONG *)m_pBits;
  755. for (x=0;x<m_nBitmapWidth;x++)
  756. {
  757. pEdgePixels[x] = BORDER_EDGE; // top row
  758. pEdgePixels[numPixels-x-1] = BORDER_EDGE; // bottom row
  759. }
  760. // vertical sides
  761. for (position=m_nBitmapWidth;position+m_nBitmapWidth<numPixels;position+=m_nBitmapWidth)
  762. {
  763. pEdgePixels[position] = BORDER_EDGE; // left edge
  764. pEdgePixels[position+m_nBitmapWidth-1] = BORDER_EDGE; // right edge
  765. }
  766. position=m_nBitmapWidth;
  767. maxEdge=maxPixel;
  768. for (y=1;y<m_nBitmapHeight-1;y++)
  769. {
  770. position++; // because we start at y=1 instead of y=0
  771. for (x=1;x<m_nBitmapWidth-1;x++)
  772. {
  773. if (pBitmapPixels[position]!=DEAD_PIXEL) // we ignore DEAD_PIXEL pixels completelly
  774. {
  775. // check for pixels to mark as not shadows
  776. if (pEdgePixels[position]!=BORDER_EDGE
  777. && Intensity(pEdgePixels[position])>min_guaranteed_not_shadow
  778. && enhanceEdges) // we only mark pixels as NOT_SHADOW if we are in enhanceEdges mode
  779. {
  780. pBitmapPixels[position]=NOT_SHADOW;
  781. }
  782. else // maybe this is a shadow pixel...
  783. if (pBitmapPixels[position]!=NOT_SHADOW
  784. && pBitmapPixels[position]!=DEAD_PIXEL
  785. && Intensity(pBitmapPixels[position])<=start
  786. && Intensity(pEdgePixels[position])<=maxEdge
  787. && pBitmapPixels[position]!=ERASEDSHADOW
  788. && DifferenceFromGray(pBitmapPixels[position])<=differenceFromGrey)
  789. { // pixel is a shadow pixel
  790. stackHeight=1;
  791. pShadowStack[0]=position;
  792. pBitmapPixels[position]=ERASEDSHADOW; // when we have decided a pixel is a shadow pixel, set it to zero
  793. // fighitng edges add extra complexity but potentially allow us greater accuracy
  794. // the concept is to mark pixels which cannot possibly be shadow pixels as such
  795. // fighting edges only come into effect if FIGHTING_EDGES is set to true and enhanceEdges is set to false
  796. // for the current KillShadows pass
  797. if (FIGHTING_EDGES)
  798. if (!enhanceEdges
  799. && Intensity(pEdgePixels[position])<=FIGHTING_EDGE_MAX_EDGE
  800. && DifferenceFromGray(pBitmapPixels[position])<=FIGHTING_EDGES_DIFF_FROM_GREY
  801. && Intensity(pBitmapPixels[position])>=FIGHTING_EDGE_MIN_MARK_PIXEL
  802. && Intensity(pBitmapPixels[position])<=FIGHTING_EDGE_MAX_MARK_PIXEL
  803. )
  804. pBitmapPixels[position]=DEAD_PIXEL;
  805. while (stackHeight>0)
  806. {
  807. searchPosition=pShadowStack[--stackHeight];
  808. searchEdge=Intensity(pEdgePixels[searchPosition]); // key idea: we are on a search and destroy mission for smooth gradients
  809. // make sure our current edge value is similar to our last edge value
  810. newPosition=searchPosition-1;
  811. if ((pBitmapPixels[newPosition]!=NOT_SHADOW)
  812. && pBitmapPixels[newPosition]!=DEAD_PIXEL
  813. && Intensity(pEdgePixels[newPosition])<=maxPixel
  814. && pBitmapPixels[newPosition]!=ERASEDSHADOW
  815. && DifferenceFromGray(pBitmapPixels[newPosition])<=differenceFromGrey)
  816. {
  817. pBitmapPixels[newPosition]=ERASEDSHADOW;
  818. if (FIGHTING_EDGES)
  819. if (!enhanceEdges
  820. && Intensity(pEdgePixels[newPosition])<=FIGHTING_EDGE_MAX_EDGE
  821. && DifferenceFromGray(pBitmapPixels[position])<=FIGHTING_EDGES_DIFF_FROM_GREY
  822. &&Intensity(pBitmapPixels[newPosition])>=FIGHTING_EDGE_MIN_MARK_PIXEL
  823. )
  824. pBitmapPixels[newPosition]=DEAD_PIXEL;
  825. pShadowStack[stackHeight++]=newPosition;
  826. }
  827. newPosition=searchPosition+1;
  828. if (pBitmapPixels[newPosition]!=NOT_SHADOW
  829. && pBitmapPixels[newPosition]!=DEAD_PIXEL
  830. && Intensity(pEdgePixels[newPosition])<=maxPixel
  831. && pBitmapPixels[newPosition]!=ERASEDSHADOW
  832. && DifferenceFromGray(pBitmapPixels[newPosition])<=differenceFromGrey)
  833. {
  834. pBitmapPixels[newPosition]=ERASEDSHADOW;
  835. if (FIGHTING_EDGES)
  836. if (!enhanceEdges
  837. && Intensity(pEdgePixels[newPosition])<=FIGHTING_EDGE_MAX_EDGE
  838. && DifferenceFromGray(pBitmapPixels[position])<=FIGHTING_EDGES_DIFF_FROM_GREY
  839. &&Intensity(pBitmapPixels[position])<=FIGHTING_EDGE_MAX_MARK_PIXEL
  840. &&Intensity(pBitmapPixels[position])>=FIGHTING_EDGE_MIN_MARK_PIXEL
  841. )
  842. pBitmapPixels[newPosition]=DEAD_PIXEL;
  843. pShadowStack[stackHeight++]=newPosition;
  844. }
  845. newPosition=searchPosition-m_nBitmapWidth;
  846. if (pBitmapPixels[newPosition]!=NOT_SHADOW
  847. && pBitmapPixels[newPosition]!=DEAD_PIXEL
  848. && Intensity(pEdgePixels[newPosition])<=maxPixel
  849. && pBitmapPixels[newPosition]!=ERASEDSHADOW
  850. && DifferenceFromGray(pBitmapPixels[newPosition])<=differenceFromGrey)
  851. {
  852. pBitmapPixels[newPosition]=ERASEDSHADOW;
  853. if (FIGHTING_EDGES)
  854. if (!enhanceEdges
  855. && Intensity(pEdgePixels[newPosition])<=FIGHTING_EDGE_MAX_EDGE
  856. && DifferenceFromGray(pBitmapPixels[position])<=FIGHTING_EDGES_DIFF_FROM_GREY
  857. &&Intensity(pBitmapPixels[newPosition])>=FIGHTING_EDGE_MIN_MARK_PIXEL
  858. &&Intensity(pBitmapPixels[position])>=FIGHTING_EDGE_MIN_MARK_PIXEL
  859. )
  860. pBitmapPixels[newPosition]=DEAD_PIXEL;
  861. pShadowStack[stackHeight++]=newPosition;
  862. }
  863. newPosition=searchPosition+m_nBitmapWidth;
  864. if (pBitmapPixels[newPosition]!=NOT_SHADOW
  865. && pBitmapPixels[newPosition]!=DEAD_PIXEL
  866. && Intensity(pEdgePixels[newPosition])<=maxPixel
  867. && pBitmapPixels[newPosition]!=ERASEDSHADOW
  868. && DifferenceFromGray(pBitmapPixels[newPosition])<=differenceFromGrey)
  869. {
  870. pBitmapPixels[newPosition]=ERASEDSHADOW;
  871. if (FIGHTING_EDGES)
  872. if (!enhanceEdges
  873. && Intensity(pEdgePixels[newPosition])<=FIGHTING_EDGE_MAX_EDGE
  874. &&Intensity(pBitmapPixels[newPosition])>=FIGHTING_EDGE_MIN_MARK_PIXEL
  875. && DifferenceFromGray(pBitmapPixels[position])<=FIGHTING_EDGES_DIFF_FROM_GREY
  876. &&Intensity(pBitmapPixels[position])>=FIGHTING_EDGE_MIN_MARK_PIXEL
  877. )
  878. pBitmapPixels[newPosition]=DEAD_PIXEL;
  879. pShadowStack[stackHeight++]=newPosition;
  880. }
  881. }
  882. }
  883. }
  884. position++;
  885. }
  886. position++;
  887. }
  888. delete[] pShadowStack;
  889. return(TRUE);
  890. }
  891. else
  892. {
  893. return(FALSE);
  894. }
  895. }
  896. // older simpler version... that includes comments:
  897. /*int C32BitDibWrapper::KillShadows(C32BitDibWrapper * edgeBitmap, UINT start, UINT maxPixel, UINT differenceFromGrey)
  898. {
  899. int x,y,position, searchPosition, newPosition;
  900. ULONG searchEdge;
  901. ULONG * pEdgePixels;
  902. ULONG * pBitmapPixels;
  903. int * pShadowStack;
  904. int stackHeight;
  905. int numPixels;
  906. UINT maxEdge;
  907. numPixels=m_nBitmapWidth*m_nBitmapHeight;
  908. pShadowStack = new int[MAXSTACK]; // we use a stack as part of a depth first search for potential shadow pixels next to a shadow pixel we have found
  909. if (pShadowStack==NULL) return(FALSE); // we probably ran out of memory. die gracefully.
  910. stackHeight=0;
  911. // we first change all the border edge pixels to white so we don't go off the edge
  912. // KillShadows avoids pixels with high shadow values so this should keep us from going off
  913. // the edge of the bitmap... if maxPixel were set to 0xffffff, this would crash
  914. // but in such a case, KillShadows would kill the whole bitmap.
  915. // this is much faster than other ways of doing bounds checking
  916. // we set all edge pixels to values such that we are gauranteed to reject them
  917. pEdgePixels=(ULONG *)edgeBitmap->m_pBits;
  918. pBitmapPixels=(ULONG *)m_pBits;
  919. // horizontal sides
  920. for (x=0;x<m_nBitmapWidth;x++)
  921. {
  922. pEdgePixels[x] = 0xffffff; // top row
  923. pEdgePixels[numPixels-x-1] = 0xffffff; // bottom row
  924. }
  925. // vertical sides
  926. for (position=m_nBitmapWidth;position+m_nBitmapWidth<numPixels;position+=m_nBitmapWidth)
  927. {
  928. pEdgePixels[position] = 0xffffff; // left edge
  929. pEdgePixels[position+m_nBitmapWidth-1] = 0xffffff; // right edge
  930. }
  931. position=m_nBitmapWidth;
  932. maxEdge=maxPixel;
  933. for (y=1;y<m_nBitmapHeight-1;y++)
  934. {
  935. position++; // because we start at y=1 instead of y=0
  936. for (x=1;x<m_nBitmapWidth-1;x++)
  937. {
  938. // we only start a shadow kill search if
  939. if (Intensity(pBitmapPixels[position])<=start && Intensity(pEdgePixels[position])<=maxEdge && pBitmapPixels[position]!=ERASEDSHADOW && DifferenceFromGray(pBitmapPixels[position])<=differenceFromGrey)
  940. {
  941. // initialize the stack to do a DFS without recursion to find shadows
  942. stackHeight=1; // we are going to place the current position on the stack
  943. pShadowStack[0]=position;
  944. pBitmapPixels[position]=ERASEDSHADOW; // when we have decided a pixel is a shadow pixel, set it to zero
  945. while (stackHeight>0)
  946. {
  947. searchPosition=pShadowStack[--stackHeight];
  948. searchEdge=Intensity(pEdgePixels[searchPosition]); // key idea: we are on a search and destroy mission for smooth gradients
  949. // make sure our current edge value is similar to our last edge value
  950. newPosition=searchPosition-1; // try the pixel to the left of the current pixel
  951. if (Intensity(pEdgePixels[newPosition])<=maxPixel
  952. && pBitmapPixels[newPosition]!=ERASEDSHADOW
  953. && DifferenceFromGray(pBitmapPixels[newPosition])<=differenceFromGrey) // requirements for classifying a pixel as a shadow pixel if we have classified an adjacent pixel as a shadow pixel
  954. {
  955. pBitmapPixels[newPosition]=ERASEDSHADOW; // delete the pixel and mark it as an erased pixel so that our search doesn't go into an infinite loop
  956. pShadowStack[stackHeight++]=newPosition;
  957. }
  958. newPosition=searchPosition+1; // try the pixel to the right of the current pixel
  959. if (Intensity(pEdgePixels[newPosition])<=maxPixel
  960. && pBitmapPixels[newPosition]!=ERASEDSHADOW
  961. && DifferenceFromGray(pBitmapPixels[newPosition])<=differenceFromGrey)
  962. {
  963. pBitmapPixels[newPosition]=ERASEDSHADOW;
  964. pShadowStack[stackHeight++]=newPosition;
  965. }
  966. newPosition=searchPosition-m_nBitmapWidth; // try the pixel directly above the current pixel
  967. if (Intensity(pEdgePixels[newPosition])<=maxPixel
  968. && pBitmapPixels[newPosition]!=ERASEDSHADOW
  969. && DifferenceFromGray(pBitmapPixels[newPosition])<=differenceFromGrey)
  970. {
  971. pBitmapPixels[newPosition]=ERASEDSHADOW;
  972. pShadowStack[stackHeight++]=newPosition;
  973. }
  974. newPosition=searchPosition+m_nBitmapWidth; // try the pixel directly below the current pixel
  975. if (Intensity(pEdgePixels[newPosition])<=maxPixel
  976. && pBitmapPixels[newPosition]!=ERASEDSHADOW
  977. && DifferenceFromGray(pBitmapPixels[newPosition])<=differenceFromGrey)
  978. {
  979. pBitmapPixels[newPosition]=ERASEDSHADOW;
  980. pShadowStack[stackHeight++]=newPosition;
  981. }
  982. }
  983. }
  984. position++;
  985. }
  986. position++;
  987. }
  988. if (pShadowStack!=NULL) delete pShadowStack;
  989. return(TRUE);
  990. }
  991. */
  992. // finds clumps of pixels above the minimum image threshold
  993. // pMap is an array which indicates which chunk each pixel in the bitmap is part of
  994. // pMap values:
  995. // 0 indicates that a pixel is not part of a chunk
  996. // VERTICAL_EDGE indicates that a pixel is not part of a chunk and that the pixel is very close to the vertical edge of the image
  997. // HORIZONTAL_EDGE is the same as vertical edge, just for horizontal edges
  998. //
  999. int C32BitDibWrapper::FindChunks(int * pMap) // return number of chunks... color the chunks on pMap
  1000. {
  1001. if (pMap && m_pBits)
  1002. {
  1003. int x,y,position, searchPosition;
  1004. ULONG * pBitmapPixels;
  1005. int * pChunkStack;
  1006. int stackHeight;
  1007. int numChunks;
  1008. int chunkSize;
  1009. int deltax, deltay;
  1010. int newPosition;
  1011. // prepare pMap
  1012. // indicate which pixels are edge pixels
  1013. // position represents the location of pixel (x,y)
  1014. // we indicate which pixels are edge pixels to prevent the search routines
  1015. // that follow from running off the edge of the bitmap
  1016. // to save time, the search routines will not keep track of x and y coordinates
  1017. // so it is neccessary to provide another and faster way of determining the bounds of the bitmap
  1018. position=0;
  1019. for (y=0;y<EDGEWIDTH;y++)
  1020. {
  1021. for (x=0;x<m_nBitmapWidth;x++)
  1022. pMap[position++]=VERTICAL_EDGE;
  1023. }
  1024. for (;y<m_nBitmapHeight-EDGEWIDTH;y++)
  1025. {
  1026. for (x=0;x<EDGEWIDTH;x++)
  1027. pMap[position++]=HORIZONTAL_EDGE;
  1028. for (;x<m_nBitmapWidth-EDGEWIDTH;x++) // for pixels that are not edge pixels, we set pMap to 0 to indicate
  1029. pMap[position++]=0; // that the pixel in question is ready to be set as part of a chunk
  1030. for (;x<m_nBitmapWidth;x++)
  1031. pMap[position++]=HORIZONTAL_EDGE;
  1032. }
  1033. for (;y<m_nBitmapHeight;y++)
  1034. {
  1035. for (x=0;x<m_nBitmapWidth;x++)
  1036. pMap[position++]=VERTICAL_EDGE;
  1037. }
  1038. // we are now ready to search for chunks
  1039. pChunkStack = NULL;
  1040. pChunkStack = new int[MAXSTACK];
  1041. if (pChunkStack == NULL) return(NULL);
  1042. stackHeight=0;
  1043. numChunks=0;
  1044. pBitmapPixels=(ULONG *)m_pBits; // its more convenient for this function to deal with pixels in 32 bit chunks instead of bytes .
  1045. // at all times we keep position set so that pBitmapPixels[position] represents the pixel at coordinates (x,y)
  1046. position=m_nBitmapWidth*EDGEWIDTH;
  1047. for (y=EDGEWIDTH;y<m_nBitmapHeight-EDGEWIDTH;y++) // check if we should start a floodfill search
  1048. // at each pixel (x,y) such that we are > EDGWIDTH from the edge
  1049. {
  1050. position+=EDGEWIDTH;
  1051. for (x=EDGEWIDTH;x<m_nBitmapWidth-EDGEWIDTH;x++)
  1052. {
  1053. // check if the pixel in question is not part of an existing chunk and that its intensity
  1054. // is greater than the minimum intensity required to be part of chunk
  1055. if (pMap[position]==0 && Intensity(pBitmapPixels[position])>MIN_CHUNK_INTENSITY)
  1056. {
  1057. // initialize stack used for doing a DFS of adjacent pixels
  1058. stackHeight=1;
  1059. pChunkStack[0]=position;
  1060. numChunks++;
  1061. chunkSize=0; // compute how many pixels are in the chunk. not used at the moment, but useful if we want
  1062. // to eliminate chunks which are too small at this stage instead of at some later point
  1063. pMap[position]=numChunks; // place this pixel in a chunk like it belongs
  1064. // continue searching for pixels while the stack is not empty
  1065. while (stackHeight>0)
  1066. {
  1067. searchPosition=pChunkStack[--stackHeight]; // pop the next pixel off the stack
  1068. chunkSize++; // increment the number of pixels in the chyunk by 1
  1069. // we check if we should add all pixels within EDGEWIDTH of the searchPosition pixel to the current chunk
  1070. // we then add any such pixels which are not edge pixels to the stack
  1071. for (deltay=-EDGEWIDTH*m_nBitmapWidth;deltay<=EDGEWIDTH*m_nBitmapWidth;deltay+=m_nBitmapWidth)
  1072. for (deltax=-EDGEWIDTH;deltax<=EDGEWIDTH;deltax++)
  1073. {
  1074. newPosition=searchPosition+deltay+deltax;
  1075. if (Intensity(pBitmapPixels[newPosition])>MIN_CHUNK_INTENSITY && pMap[newPosition]<=0)
  1076. {
  1077. if (pMap[newPosition]==0) // not an edge pixel
  1078. {
  1079. pChunkStack[stackHeight++]=newPosition;
  1080. pMap[newPosition]=numChunks; // mark the pixel as part of the chunk so that we do not go into an infinite loop
  1081. }
  1082. else // if a pixel is an edge pixel, we do not want to add that pixel to the stack
  1083. {
  1084. // (because of problems with scanners with black borders)
  1085. // furthermore... we only want to add an edge pixel if the current pixel is
  1086. // in a vertical or horizontal line with the edge pixel under consideration
  1087. // as this further minimizes problems related to black scanner edges
  1088. if (pMap[newPosition]==VERTICAL_EDGE)
  1089. {
  1090. if (deltax==0) // to minimize distortion due to black outside edge
  1091. pMap[newPosition]=numChunks;
  1092. }
  1093. else // HORIZONTAL_EDGE
  1094. {
  1095. if (deltay==0) // to minimize distortion due to black outside edge
  1096. pMap[newPosition]=numChunks;
  1097. }
  1098. }
  1099. }
  1100. }
  1101. }
  1102. }
  1103. position++;
  1104. }
  1105. position+=EDGEWIDTH;
  1106. }
  1107. delete[] pChunkStack;
  1108. return(numChunks);
  1109. }
  1110. else
  1111. {
  1112. return(0);
  1113. }
  1114. }
  1115. // for debugging purposes only
  1116. // displays where the chunks denoted by pMap are on the C32BitDibWrapper
  1117. // pMap must have the same dimensions as the current bitmap or this function will fail
  1118. void C32BitDibWrapper::ColorChunks(int *pMap)
  1119. { // color in the bitmap given the region map... for debugging purposes;
  1120. if (m_pBits && pMap)
  1121. {
  1122. ULONG* pBitmapPixels;
  1123. ULONG mapColor;
  1124. int x,y;
  1125. int position;
  1126. position=0;
  1127. pBitmapPixels=(ULONG *)m_pBits;
  1128. // loop through all pixels
  1129. for (y=0;y<m_nBitmapHeight;y++)
  1130. for (x=0;x<m_nBitmapWidth;x++)
  1131. {
  1132. if (pMap[position]>0) // are we part of a region?
  1133. {
  1134. mapColor=(((ULONG)pMap[position])*431234894)&0xffffff; // a poor man's random number generator
  1135. // if we cared about speed... we should make a lookup table instead
  1136. // but this function is only for debugging purposes
  1137. pBitmapPixels[position]=((pBitmapPixels[position] & 0xfefefe)>>1)+((mapColor& 0xfefefe)>>1); // average with slight loss
  1138. }
  1139. if (pMap[position]<0) pBitmapPixels[position]=0xffffff; // color in vertical and horizontal edges
  1140. position++;
  1141. }
  1142. }
  1143. }
  1144. // designed mainly for debugging purposes... this is a painfully slow way to draw a 32 bit dib
  1145. // because of the slow conversion step from 32 bits back to 24 bits
  1146. int C32BitDibWrapper::Draw(HDC hdc, int x, int y)
  1147. {
  1148. if (hdc && m_pBits)
  1149. {
  1150. BITMAPINFO BitmapInfo;
  1151. SetBMI(&BitmapInfo,m_nBitmapWidth, m_nBitmapHeight, 24);
  1152. BYTE* pDibData = ConvertBitmap(m_pBits,32,24);
  1153. if (pDibData)
  1154. {
  1155. StretchDIBits(hdc,
  1156. x,y,m_nBitmapWidth,m_nBitmapHeight,
  1157. 0,0,m_nBitmapWidth,m_nBitmapHeight,
  1158. pDibData,
  1159. &BitmapInfo,BI_RGB,SRCCOPY);
  1160. //
  1161. // destroy the temp 24 bit dib
  1162. //
  1163. delete[] pDibData;
  1164. return(TRUE);
  1165. }
  1166. }
  1167. return(FALSE);
  1168. }
  1169. // set pixel and get pixel are completelly unoptimized
  1170. // If you wish to make them faster, create a table of values storing y*m_nBitmapWidth for all values of y
  1171. // as GetPixel is used by the line drawing functions, this could result in a signifigant speed up as
  1172. // the line drawing functions are used for region collision detection
  1173. void inline C32BitDibWrapper::SetPixel(int x, int y, ULONG color)
  1174. {
  1175. if (m_pBits)
  1176. {
  1177. ULONG* pBitmapPixels=(ULONG*)m_pBits;
  1178. pBitmapPixels[y*m_nBitmapWidth+x]=color;
  1179. }
  1180. }
  1181. ULONG inline C32BitDibWrapper::GetPixel(int x, int y)
  1182. {
  1183. if (m_pBits)
  1184. {
  1185. ULONG* pBitmapPixels=(ULONG*)m_pBits;
  1186. return(pBitmapPixels[y*m_nBitmapWidth+x]);
  1187. }
  1188. return 0;
  1189. }
  1190. //
  1191. // calculates the total intensity along a line
  1192. //
  1193. // Line drawing code modified from VGA line drawing code from Michael
  1194. // Abrash's Graphics Programming Black Book
  1195. //
  1196. // this is the one function which I did not create from scratch so any bug
  1197. // questions should be directed to Michael Abrash =) why reinvent the wheel
  1198. // when Bresenham line drawing is kindof hard to beat. particularly in this
  1199. // case when we are using lines as tracers so we couldn't care less about if
  1200. // they are antialiased or otherwise made to look less jagged.
  1201. //
  1202. // t-jacobr
  1203. //
  1204. ULONG C32BitDibWrapper::Line(int X0, int Y0,int X1, int Y1)
  1205. {
  1206. if (m_pBits)
  1207. {
  1208. if (X0<0) X0=0;
  1209. if (Y0<0) Y0=0;
  1210. if (X1<0) X1=0;
  1211. if (Y1<0) Y1=0;
  1212. if (X0>=m_nBitmapWidth) X0=m_nBitmapWidth;
  1213. if (Y0>=m_nBitmapHeight) Y0=m_nBitmapHeight;
  1214. if (X1>=m_nBitmapWidth) X1=m_nBitmapWidth;
  1215. if (Y1>=m_nBitmapHeight) Y1=m_nBitmapHeight;
  1216. int DeltaX, DeltaY;
  1217. int Temp;
  1218. if (Y0>Y1)
  1219. {
  1220. Temp=Y0;
  1221. Y0=Y1;
  1222. Y1=Temp;
  1223. Temp = X0;
  1224. X0=X1;
  1225. X1=Temp;
  1226. }
  1227. DeltaX=X1-X0;
  1228. DeltaY=Y1-Y0;
  1229. if (DeltaX>0)
  1230. {
  1231. if (DeltaX>DeltaY)
  1232. {
  1233. return(Octant0(X0,Y0,DeltaX,DeltaY,1));
  1234. }
  1235. else
  1236. {
  1237. return(Octant1(X0,Y0,DeltaX,DeltaY,1));
  1238. }
  1239. }
  1240. else
  1241. {
  1242. DeltaX = -DeltaX;
  1243. if (DeltaX>DeltaY)
  1244. {
  1245. return(Octant0(X0,Y0,DeltaX,DeltaY,-1));
  1246. }
  1247. else
  1248. {
  1249. return(Octant1(X0,Y0,DeltaX,DeltaY,-1));
  1250. }
  1251. }
  1252. }
  1253. else
  1254. {
  1255. return(0); // invalid bitmap
  1256. }
  1257. }
  1258. // helper functions for line drawing
  1259. // these aint no normal nine drawing functions
  1260. // what we do is we calculate the total intensity of all of the above threshold pixels along the line
  1261. ULONG C32BitDibWrapper::Octant0(int X0, int Y0,int DeltaX,int DeltaY,int XDirection)
  1262. {
  1263. if (IsValid())
  1264. {
  1265. int DeltaYx2;
  1266. int DeltaYx2MinusDeltaXx2;
  1267. int ErrorTerm;
  1268. ULONG totalIntensity;
  1269. ULONG pixelIntensity;
  1270. totalIntensity=0;
  1271. DeltaYx2=DeltaY*2;
  1272. DeltaYx2MinusDeltaXx2=DeltaYx2 - (DeltaX*2);
  1273. ErrorTerm = DeltaYx2 - DeltaX;
  1274. // SetPixel(X0,Y0,0x0000ff);
  1275. while (2<DeltaX--) // skip the last pixel
  1276. {
  1277. if (ErrorTerm >=0)
  1278. {
  1279. Y0++;
  1280. ErrorTerm +=DeltaYx2MinusDeltaXx2;
  1281. }
  1282. else
  1283. {
  1284. ErrorTerm +=DeltaYx2;
  1285. }
  1286. X0+=XDirection;
  1287. //SetPixel(X0,Y0,0x0000ff);
  1288. pixelIntensity=Intensity(GetPixel(X0,Y0));
  1289. if (pixelIntensity>MIN_CHUNK_INTENSITY && pixelIntensity<COLLISION_DETECTION_HIGHPASS_VALUE) totalIntensity+=512;//pixelIntensity;
  1290. }
  1291. return(totalIntensity);
  1292. }
  1293. return 0;
  1294. }
  1295. ULONG C32BitDibWrapper::Octant1(int X0, int Y0, int DeltaX, int DeltaY, int XDirection)
  1296. {
  1297. if (IsValid())
  1298. {
  1299. int DeltaXx2;
  1300. int DeltaXx2MinusDeltaYx2;
  1301. int ErrorTerm;
  1302. ULONG totalIntensity;
  1303. ULONG pixelIntensity;
  1304. totalIntensity=0;
  1305. DeltaXx2 = DeltaX * 2;
  1306. DeltaXx2MinusDeltaYx2 = DeltaXx2 - (DeltaY*2);
  1307. ErrorTerm = DeltaXx2 - DeltaY;
  1308. //SetPixel(X0,Y0,0x0000ff);
  1309. while (2<DeltaY--)
  1310. { // skip last pixel
  1311. if (ErrorTerm >=0)
  1312. {
  1313. X0 +=XDirection;
  1314. ErrorTerm +=DeltaXx2MinusDeltaYx2;
  1315. }
  1316. else
  1317. {
  1318. ErrorTerm +=DeltaXx2;
  1319. }
  1320. Y0++;
  1321. pixelIntensity=Intensity(GetPixel(X0,Y0));
  1322. if (pixelIntensity>MIN_CHUNK_INTENSITY && pixelIntensity<COLLISION_DETECTION_HIGHPASS_VALUE) totalIntensity+=512;//pixelIntensity;
  1323. }
  1324. return(totalIntensity);
  1325. }
  1326. return 0;
  1327. }
  1328. //
  1329. // Compensate for Background Color could be made more than 3 times as fast if needed by caculating each pixel
  1330. // using 1 cycle instead of 3.
  1331. void C32BitDibWrapper::CompensateForBackgroundColor(int r, int g, int b)
  1332. {
  1333. if (IsValid())
  1334. {
  1335. int nNumBits=m_nBitmapWidth*m_nBitmapHeight*4;
  1336. for (int position=0;position<nNumBits;position+=4)
  1337. {
  1338. if (r<m_pBits[position]) m_pBits[position]=m_pBits[position]-r;
  1339. else m_pBits[position]=0;
  1340. if (g<m_pBits[position+1]) m_pBits[position+1]=m_pBits[position+1]-g;
  1341. else m_pBits[position+1]=0;
  1342. if (b<m_pBits[position+2]) m_pBits[position+2]=m_pBits[position+2]-b;
  1343. else m_pBits[position+2]=0;
  1344. }
  1345. }
  1346. }
  1347. // invert the bitmap
  1348. void C32BitDibWrapper::Invert(void)
  1349. {
  1350. if (IsValid())
  1351. {
  1352. int numPixels;
  1353. int i;
  1354. ULONG* pBitmapPixels;
  1355. pBitmapPixels=(ULONG*)m_pBits; // operate in 32 bit chunks instead of 8 bit chunks
  1356. numPixels=m_nBitmapWidth*m_nBitmapHeight;
  1357. // loop through all pixels in the bitmap
  1358. for (i=0;i<numPixels;i++)
  1359. pBitmapPixels[i]^=0xffffff; // flipping all bits inverts the pixel
  1360. }
  1361. }
  1362. // note: we could get some wierd effects because despeckle edits the bitmap its examining
  1363. // but this shouldn't be a signifigant problem and often, the self referential aspect only acts to slightly increase accuracy
  1364. // this function is not the same as the standard photoshop despeckle filter.
  1365. // we only care about a small category of stray dots.
  1366. // stray dots which are surrounded by white pixels (or pixels which have been eliminated by remove shadow filters)
  1367. void C32BitDibWrapper::Despeckle(void)
  1368. {
  1369. if (IsValid())
  1370. {
  1371. ULONG* pBitmapPixels;
  1372. int numPixels;
  1373. int position;
  1374. int x,y;
  1375. pBitmapPixels=(ULONG*)m_pBits;
  1376. numPixels=m_nBitmapWidth*m_nBitmapHeight;
  1377. position=4*m_nBitmapWidth;
  1378. // loop through all pixels which are not border pixels
  1379. // pBitmapPixels[position] should be the pixel at (x,y) in all cases
  1380. for (y=4;y<m_nBitmapHeight-4;y++)
  1381. {
  1382. position+=4;
  1383. for (x=4;x<m_nBitmapWidth-4;x++)
  1384. {
  1385. DespecklePixel(pBitmapPixels, position,false);
  1386. position++;
  1387. }
  1388. position+=4;
  1389. }
  1390. }
  1391. }
  1392. // we may want to despeckle the edges of an image more often than the rest of the image
  1393. // as image edges are often trouble spots...
  1394. // because of this, we should recommend that users place images in the center of the scanner
  1395. // when doing region detection to increase accuracy.
  1396. // the concept we are applying is that when we have to make sacrifices we make sacrifices in areas where we hurt cases that would have been very very hard anyway.
  1397. void C32BitDibWrapper::EdgeDespeckle(void)
  1398. {
  1399. if (IsValid())
  1400. {
  1401. ULONG* pBitmapPixels;
  1402. int x,y,position;
  1403. pBitmapPixels=(ULONG*)m_pBits;
  1404. position=m_nBitmapWidth*4;
  1405. // top edge
  1406. // as always, at all times we insure that pBitmapPixels[position] is the pixel at (x,y)
  1407. for (y=4;y<DESPECKLE_BORDER_WIDTH+4;y++)
  1408. {
  1409. position+=4;
  1410. for (x=4;x<m_nBitmapWidth-4;x++)
  1411. {
  1412. DespecklePixel(pBitmapPixels, position,true);
  1413. position++;
  1414. }
  1415. position+=4;
  1416. }
  1417. // side edges
  1418. for (;y<m_nBitmapHeight-DESPECKLE_BORDER_WIDTH-4;y++)
  1419. {
  1420. position+=4;
  1421. for (x=4;x<DESPECKLE_BORDER_WIDTH+4;x++)
  1422. {
  1423. DespecklePixel(pBitmapPixels, position,true); // left edge
  1424. DespecklePixel(pBitmapPixels, position+m_nBitmapWidth-DESPECKLE_BORDER_WIDTH-8,true); // right edge
  1425. position++;
  1426. }
  1427. position+=m_nBitmapWidth-DESPECKLE_BORDER_WIDTH-4;
  1428. }
  1429. // bottom edge
  1430. for (;y<m_nBitmapHeight-4;y++)
  1431. {
  1432. position+=4;
  1433. for (x=4;x<m_nBitmapWidth-4;x++)
  1434. {
  1435. DespecklePixel(pBitmapPixels, position,true);
  1436. position++;
  1437. }
  1438. position+=4;
  1439. }
  1440. }
  1441. }
  1442. // given the pixel at position i, figure out if it meets any of the requirements for eliminating the pixel
  1443. // if it does, eliminate the pixel. edgePixel specifies if the pixel is an edgePixel (in which case we may want
  1444. // to apply more strict requirements).
  1445. void C32BitDibWrapper::DespecklePixel(ULONG* pBitmapPixels, int i, bool edgePixel)
  1446. {
  1447. if (IsValid())
  1448. {
  1449. if (Intensity(pBitmapPixels[i])>MIN_CHUNK_INTENSITY)
  1450. {
  1451. // deletes:
  1452. //
  1453. // xx
  1454. // xx
  1455. //
  1456. if (
  1457. Intensity(pBitmapPixels[i-1-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1458. && Intensity(pBitmapPixels[i-1])<MIN_CHUNK_INTENSITY
  1459. && Intensity(pBitmapPixels[i-1+m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1460. && Intensity(pBitmapPixels[i-1+m_nBitmapWidth*2])<MIN_CHUNK_INTENSITY
  1461. && Intensity(pBitmapPixels[i+2-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1462. && Intensity(pBitmapPixels[i+2])<MIN_CHUNK_INTENSITY
  1463. && Intensity(pBitmapPixels[i+2+m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1464. && Intensity(pBitmapPixels[i+2+m_nBitmapWidth*2])<MIN_CHUNK_INTENSITY
  1465. && Intensity(pBitmapPixels[i-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1466. && Intensity(pBitmapPixels[i+1-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1467. && Intensity(pBitmapPixels[i+m_nBitmapWidth*2])<MIN_CHUNK_INTENSITY
  1468. && Intensity(pBitmapPixels[i+1+m_nBitmapWidth*2])<MIN_CHUNK_INTENSITY
  1469. )
  1470. {
  1471. pBitmapPixels[i]=0;
  1472. pBitmapPixels[i+1]=0;
  1473. pBitmapPixels[i+m_nBitmapWidth]=0;
  1474. pBitmapPixels[i+1+m_nBitmapWidth]=0;
  1475. }
  1476. if (edgePixel==true)
  1477. {
  1478. // radius one speckle
  1479. // horizontal despeckle
  1480. if (Intensity(pBitmapPixels[i-1])<MIN_CHUNK_INTENSITY
  1481. && Intensity(pBitmapPixels[i-2])<MIN_CHUNK_INTENSITY
  1482. && Intensity(pBitmapPixels[i+2])<MIN_CHUNK_INTENSITY
  1483. && Intensity(pBitmapPixels[i+1])<MIN_CHUNK_INTENSITY)
  1484. pBitmapPixels[i]=0; // despeckle the speckle
  1485. // vertical despeckle
  1486. if (Intensity(pBitmapPixels[i-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1487. && Intensity(pBitmapPixels[i-m_nBitmapWidth*2])<MIN_CHUNK_INTENSITY
  1488. && Intensity(pBitmapPixels[i+m_nBitmapWidth*2])<MIN_CHUNK_INTENSITY
  1489. && Intensity(pBitmapPixels[i+m_nBitmapWidth])<MIN_CHUNK_INTENSITY)
  1490. pBitmapPixels[i]=0; // despeckle the speckle
  1491. // radius two despeckle
  1492. if (Intensity(pBitmapPixels[i-2])<MIN_CHUNK_INTENSITY
  1493. && Intensity(pBitmapPixels[i-3])<MIN_CHUNK_INTENSITY
  1494. && Intensity(pBitmapPixels[i+2])<MIN_CHUNK_INTENSITY
  1495. && Intensity(pBitmapPixels[i+3])<MIN_CHUNK_INTENSITY)
  1496. pBitmapPixels[i]=0; // despeckle the speckle
  1497. // vertical despeckle
  1498. if (Intensity(pBitmapPixels[i-m_nBitmapWidth*2])<MIN_CHUNK_INTENSITY
  1499. && Intensity(pBitmapPixels[i-m_nBitmapWidth*3])<MIN_CHUNK_INTENSITY
  1500. && Intensity(pBitmapPixels[i+m_nBitmapWidth*2])<MIN_CHUNK_INTENSITY
  1501. && Intensity(pBitmapPixels[i+m_nBitmapWidth*3])<MIN_CHUNK_INTENSITY)
  1502. pBitmapPixels[i]=0; // despeckle the speckle
  1503. // despeckle to eliminate clumps like this:
  1504. // clump: ? ?
  1505. // x
  1506. // ? ?
  1507. if (Intensity(pBitmapPixels[i-1-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1508. && Intensity(pBitmapPixels[i+1-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1509. && Intensity(pBitmapPixels[i-1+m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1510. && Intensity(pBitmapPixels[i+1+m_nBitmapWidth])<MIN_CHUNK_INTENSITY)
  1511. pBitmapPixels[i]=0; // despeckle the speckle
  1512. }
  1513. // to eliminate this clump:
  1514. // ?
  1515. // ?x?
  1516. // ?
  1517. //
  1518. if (Intensity(pBitmapPixels[i-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1519. && Intensity(pBitmapPixels[i+m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1520. && Intensity(pBitmapPixels[i-1])<MIN_CHUNK_INTENSITY
  1521. && Intensity(pBitmapPixels[i+1])<MIN_CHUNK_INTENSITY)
  1522. pBitmapPixels[i]=0; // despeckle the speckle
  1523. // these functions are insanely slow... if they become a major speed bottlekneck, they can be made
  1524. // 10x faster
  1525. // radius one speckle 3 pixel search depth
  1526. // horizontal despeckle
  1527. if (
  1528. Intensity(pBitmapPixels[i-1])<MIN_CHUNK_INTENSITY
  1529. && Intensity(pBitmapPixels[i-2])<MIN_CHUNK_INTENSITY
  1530. && Intensity(pBitmapPixels[i-3])<MIN_CHUNK_INTENSITY
  1531. && Intensity(pBitmapPixels[i-4])<MIN_CHUNK_INTENSITY
  1532. && Intensity(pBitmapPixels[i+4])<MIN_CHUNK_INTENSITY
  1533. && Intensity(pBitmapPixels[i+3])<MIN_CHUNK_INTENSITY
  1534. && Intensity(pBitmapPixels[i+2])<MIN_CHUNK_INTENSITY
  1535. && Intensity(pBitmapPixels[i+1])<MIN_CHUNK_INTENSITY
  1536. && Intensity(pBitmapPixels[i-1+m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1537. && Intensity(pBitmapPixels[i-2+m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1538. && Intensity(pBitmapPixels[i-3+m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1539. && Intensity(pBitmapPixels[i-4+m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1540. && Intensity(pBitmapPixels[i+4+m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1541. && Intensity(pBitmapPixels[i+3+m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1542. && Intensity(pBitmapPixels[i+2+m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1543. && Intensity(pBitmapPixels[i+1+m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1544. && Intensity(pBitmapPixels[i-1-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1545. && Intensity(pBitmapPixels[i-2-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1546. && Intensity(pBitmapPixels[i-3-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1547. && Intensity(pBitmapPixels[i-4-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1548. && Intensity(pBitmapPixels[i+4-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1549. && Intensity(pBitmapPixels[i+3-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1550. && Intensity(pBitmapPixels[i+2-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1551. && Intensity(pBitmapPixels[i+1-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1552. )
  1553. pBitmapPixels[i]=0; // despeckle the speckle
  1554. // vertical despeckle
  1555. if (
  1556. Intensity(pBitmapPixels[i-m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1557. && Intensity(pBitmapPixels[i-m_nBitmapWidth*2])<MIN_CHUNK_INTENSITY
  1558. && Intensity(pBitmapPixels[i+m_nBitmapWidth*2])<MIN_CHUNK_INTENSITY
  1559. && Intensity(pBitmapPixels[i-m_nBitmapWidth*3])<MIN_CHUNK_INTENSITY
  1560. && Intensity(pBitmapPixels[i+m_nBitmapWidth*3])<MIN_CHUNK_INTENSITY
  1561. && Intensity(pBitmapPixels[i-m_nBitmapWidth*4])<MIN_CHUNK_INTENSITY
  1562. && Intensity(pBitmapPixels[i+m_nBitmapWidth*4])<MIN_CHUNK_INTENSITY
  1563. && Intensity(pBitmapPixels[i+m_nBitmapWidth])<MIN_CHUNK_INTENSITY
  1564. && Intensity(pBitmapPixels[i-m_nBitmapWidth+1])<MIN_CHUNK_INTENSITY
  1565. && Intensity(pBitmapPixels[i-m_nBitmapWidth*2+1])<MIN_CHUNK_INTENSITY
  1566. && Intensity(pBitmapPixels[i+m_nBitmapWidth*2+1])<MIN_CHUNK_INTENSITY
  1567. && Intensity(pBitmapPixels[i-m_nBitmapWidth*3+1])<MIN_CHUNK_INTENSITY
  1568. && Intensity(pBitmapPixels[i+m_nBitmapWidth*3+1])<MIN_CHUNK_INTENSITY
  1569. && Intensity(pBitmapPixels[i-m_nBitmapWidth*4+1])<MIN_CHUNK_INTENSITY
  1570. && Intensity(pBitmapPixels[i+m_nBitmapWidth*4+1])<MIN_CHUNK_INTENSITY
  1571. && Intensity(pBitmapPixels[i+m_nBitmapWidth+1])<MIN_CHUNK_INTENSITY
  1572. && Intensity(pBitmapPixels[i-m_nBitmapWidth-1])<MIN_CHUNK_INTENSITY
  1573. && Intensity(pBitmapPixels[i-m_nBitmapWidth*2-1])<MIN_CHUNK_INTENSITY
  1574. && Intensity(pBitmapPixels[i+m_nBitmapWidth*2-1])<MIN_CHUNK_INTENSITY
  1575. && Intensity(pBitmapPixels[i-m_nBitmapWidth*3-1])<MIN_CHUNK_INTENSITY
  1576. && Intensity(pBitmapPixels[i+m_nBitmapWidth*3-1])<MIN_CHUNK_INTENSITY
  1577. && Intensity(pBitmapPixels[i-m_nBitmapWidth*4-1])<MIN_CHUNK_INTENSITY
  1578. && Intensity(pBitmapPixels[i+m_nBitmapWidth*4-1])<MIN_CHUNK_INTENSITY
  1579. && Intensity(pBitmapPixels[i+m_nBitmapWidth-1])<MIN_CHUNK_INTENSITY
  1580. )
  1581. pBitmapPixels[i]=0; // despeckle the speckle
  1582. }
  1583. }
  1584. }
  1585. // its easy to correct for if the user adjusted brightness contrast
  1586. // or worse... if the scanners gamma settings are off
  1587. // not unlikely if they have a really old or really cheap scanner
  1588. void C32BitDibWrapper::CorrectBrightness(void)
  1589. {
  1590. if (IsValid())
  1591. {
  1592. int r,g,b;
  1593. int position;
  1594. int nNumBits;
  1595. r=255;
  1596. g=255;
  1597. b=255;
  1598. nNumBits=m_nBitmapWidth*(m_nBitmapHeight-4)*4;
  1599. // find the minimum, r, g, and b values;
  1600. for (position=m_nBitmapWidth*4;position<nNumBits;position+=4)
  1601. {
  1602. if (r>m_pBits[position]) r=m_pBits[position];
  1603. if (g>m_pBits[position+1]) g=m_pBits[position+1];
  1604. if (b>m_pBits[position+2]) b=m_pBits[position+2];
  1605. }
  1606. if (r!=0 || g!=0 || b!=0) // if the r, g, or b vals are off, correct them
  1607. CompensateForBackgroundColor(r,g,b);
  1608. }
  1609. }
  1610. //
  1611. // stretch out the color spectrum if the darn user adjusted brightness so that no parts of the image are black anymore
  1612. // otherwise we can get embarassing failures if the user simply tweaks brightness and contrast too much
  1613. //
  1614. // stretches upwards... if you need to compensate downwards, call correctBrightness first
  1615. void C32BitDibWrapper::MaxContrast(UINT numPixelsRequired)
  1616. {
  1617. if (IsValid())
  1618. {
  1619. int position;
  1620. int nNumBits;
  1621. int max;
  1622. int i;
  1623. int temp;
  1624. BYTE pConversionTable[256];
  1625. ULONG pNum[256];
  1626. for (i=0;i<256;i++)
  1627. pNum[i]=0;
  1628. nNumBits=m_nBitmapWidth*m_nBitmapHeight*4;
  1629. // compute the number of pixels of each intensity level
  1630. for (position=0;position<nNumBits;position+=4)
  1631. {
  1632. pNum[m_pBits[position]]++;
  1633. pNum[m_pBits[position+1]]++;
  1634. pNum[m_pBits[position+2]]++;
  1635. }
  1636. max=1;
  1637. // find max intensity which has at least numPixelsRequired of that intensity
  1638. for (i=1;i<256;i++)
  1639. if (pNum[i]>numPixelsRequired) max=i;
  1640. // create conversion table
  1641. for (i=0;i<256;i++)
  1642. {
  1643. temp=(255*i)/max;
  1644. if (temp>255) temp=255; // high pass
  1645. pConversionTable[i]=(BYTE)temp;
  1646. }
  1647. // now apply the conversion table to all pixels in the image
  1648. for (position=0;position<nNumBits;position+=4)
  1649. {
  1650. m_pBits[position]=pConversionTable[m_pBits[position]];
  1651. m_pBits[position+1]=pConversionTable[m_pBits[position+1]];
  1652. m_pBits[position+2]=pConversionTable[m_pBits[position+2]];
  1653. }
  1654. }
  1655. }
  1656. /// we don't want to use intensity here
  1657. // because this is a function used for detecting text regions
  1658. // and text regions are more likely to have grey background than yellow backgrounds
  1659. // hence doing a chan by chan test is more effective
  1660. // IMPORTANT NOTE: this function is designed for use with a non-inverted bitmap unlike most of the other functions in this library
  1661. int C32BitDibWrapper::PixelsBelowThreshold(C32BitDibWrapper* pProccessedBitmap, C32BitDibWrapper * pEdgesBitmap, RECT region)
  1662. {
  1663. if (IsValid() && pProccessedBitmap && pEdgesBitmap && pProccessedBitmap->IsValid() && pEdgesBitmap->IsValid())
  1664. {
  1665. int x,y;
  1666. int position;
  1667. int numPixels;
  1668. ULONG* pBitmapPixels;
  1669. ULONG* pEdgePixels;
  1670. ULONG* pProccessedPixels; // bitmap with shadows removed, etc
  1671. // we assume that the edge bitmap has the same width and height as this bitmap to shave a couple of 1/1000ths of a second... and cause we are lazy
  1672. numPixels=0;
  1673. pBitmapPixels=(ULONG *)m_pBits;
  1674. pEdgePixels=(ULONG *)(pEdgesBitmap->m_pBits);
  1675. pProccessedPixels=(ULONG *)(pProccessedBitmap->m_pBits);
  1676. position=region.top*m_nBitmapWidth;
  1677. // search through all pixels in the region
  1678. // at all times, pBitmapPixels[position] is the pixel at point (x,y)
  1679. for (y=region.top;y<=region.bottom;y++)
  1680. {
  1681. position+=region.left;
  1682. for (x=region.left;x<=region.right;x++)
  1683. {
  1684. if ((
  1685. (pBitmapPixels[position]&0xff) > TEXT_REGION_BACKGROUND_THRESHOLD
  1686. && (pBitmapPixels[position]&0xff00) > (TEXT_REGION_BACKGROUND_THRESHOLD<<8)
  1687. && (pBitmapPixels[position]&0xff0000)> (TEXT_REGION_BACKGROUND_THRESHOLD<<16) // below threshold
  1688. && Intensity(pEdgePixels[position]) > MIN_TEXT_REGION_BACKGROUND_EDGE) // does it have the requisite edge val?
  1689. || (pProccessedPixels[position]==0
  1690. && Intensity(pEdgePixels[position])>MIN_TEXT_REGION_BACKGROUND_EDGE_CLIPPED_PIXEL
  1691. && (pBitmapPixels[position]&0xff) > CLIPPED_TEXT_REGION_BACKGROUND_THRESHOLD
  1692. && (pBitmapPixels[position]&0xff00) > (CLIPPED_TEXT_REGION_BACKGROUND_THRESHOLD<<8)
  1693. && (pBitmapPixels[position]&0xff0000)> (CLIPPED_TEXT_REGION_BACKGROUND_THRESHOLD<<16) // below threshold
  1694. )) // we coulda been a dead shadow pixel too.. this is risky because depending on the settings, we may have culled plenty of deserving pixels
  1695. {
  1696. // we hold pixels to much higher standards if they are clipped pixels... to avoid too much stray clipping
  1697. numPixels++;
  1698. }
  1699. position++;
  1700. }
  1701. position+=m_nBitmapWidth-region.right-1;
  1702. }
  1703. return(numPixels);
  1704. }
  1705. else
  1706. {
  1707. return(0); // invalid bitmap
  1708. }
  1709. }
  1710. // the name of the game here is whatever works
  1711. // this function may be ugly, but its the easiest way to get rid of
  1712. // black borders without hurting overly many innocent pixels
  1713. void C32BitDibWrapper::RemoveBlackBorder(int minBlackBorderPixel, C32BitDibWrapper * outputBitmap, C32BitDibWrapper * debugBitmap)
  1714. {
  1715. if (IsValid() && m_nBitmapWidth>100 && m_nBitmapHeight>100 && outputBitmap) // these tests are designed for reasonably large bitmaps
  1716. {
  1717. // bottom border
  1718. KillBlackBorder(minBlackBorderPixel,m_nBitmapWidth*m_nBitmapHeight-m_nBitmapWidth,m_nBitmapWidth,m_nBitmapHeight,1,-m_nBitmapWidth, outputBitmap, debugBitmap);
  1719. // top border
  1720. KillBlackBorder(minBlackBorderPixel,0,m_nBitmapWidth,m_nBitmapHeight,1,m_nBitmapWidth, outputBitmap, debugBitmap);
  1721. // left side
  1722. KillBlackBorder(minBlackBorderPixel,0,m_nBitmapHeight,m_nBitmapWidth, m_nBitmapWidth,1, outputBitmap, debugBitmap);
  1723. // right side
  1724. KillBlackBorder(minBlackBorderPixel,m_nBitmapWidth-1,m_nBitmapHeight,m_nBitmapWidth, m_nBitmapWidth,-1, outputBitmap, debugBitmap);
  1725. }
  1726. }
  1727. // this function encapsulates the single purpose algorithm used to
  1728. // remove particularly troublesome shadows from the sides of images
  1729. // this function is poorly tweaked and it iss very likely that we could either
  1730. // greatly improve the number of errors detected
  1731. // or the number of false errors that are unfairly zapped
  1732. // debugBitmap is edited to give a graphical representation of which shadows have been eliminated
  1733. // debugBitmap is only edited if the VISUAL_DEBUG flag is set
  1734. // as shown in RemoveBlackBorder, KillBlackBorder is called with different startPosition, width, height, dx, and dy values
  1735. // depending on whether we are working on the top border, the left border, the right border, or the bottom border.
  1736. // from the perspective of KillBlackBorder, it is working on eliminating shadows from a bitmap which is width pixels wide
  1737. // height pixels high and the location of pixel (0,0) is startPosition. Where to move one pixel in the x direction
  1738. // you increment startPosition by dx and to move one pixel in the y direction, you increment dy by 1.
  1739. void C32BitDibWrapper::KillBlackBorder(int minBlackBorderPixel, int startPosition, int width, int height, int dx, int dy, C32BitDibWrapper *pOutputBitmap, C32BitDibWrapper * pDebugBitmap)
  1740. {
  1741. if (IsValid() && pOutputBitmap && pOutputBitmap->IsValid() && width>100 && height>100)
  1742. {
  1743. int x,y,position, searchPosition, newPosition;
  1744. ULONG * pBitmapPixels;
  1745. int endPoint;
  1746. int r,g,b;
  1747. int dr,dg,db;
  1748. int i;
  1749. int sourceR,sourceG, sourceB;
  1750. int errors;
  1751. int step;
  1752. int* pShadowDepths;
  1753. int* pTempShadowDepths;
  1754. int longestBackgroundPixelString;
  1755. int borderPixels;
  1756. int nonBackgroundPixels;
  1757. int backgroundPixels;
  1758. BYTE* pBlurredBits = m_pBits;
  1759. ULONG* pDebugPixels;
  1760. BYTE* pOutputBits;
  1761. pOutputBits=pOutputBitmap->m_pBits;
  1762. pShadowDepths=new int[width]; // we keep an array of how many pixels we think the black border is for each scan line
  1763. if (pShadowDepths==NULL) return;
  1764. pTempShadowDepths=NULL;
  1765. pTempShadowDepths=new int[width];
  1766. if (pTempShadowDepths==NULL)
  1767. {
  1768. delete[] pShadowDepths;
  1769. return;
  1770. }
  1771. int numPixels=height*width; // total pixels in the image
  1772. pBitmapPixels=(ULONG *)(pOutputBitmap->m_pBits);
  1773. if (pBitmapPixels)
  1774. {
  1775. pDebugPixels=(ULONG *)(pDebugBitmap->m_pBits);
  1776. step=dy*4; // when dealing with data in 8 bit chunks instead of 32 bit chunks, we need to multiply the dy step by 4
  1777. // reset all vals to 0
  1778. for (i=0;i<width;i++)
  1779. {
  1780. pShadowDepths[i]=0;
  1781. pTempShadowDepths[i]=0;
  1782. }
  1783. position=startPosition*4;
  1784. for (x=0;x<width;x++) // loop through all pixels on the top row of the image
  1785. {
  1786. r=pBlurredBits[position];
  1787. g=pBlurredBits[position+1];
  1788. b=pBlurredBits[position+2];
  1789. if (r>minBlackBorderPixel&&g>minBlackBorderPixel&&b>minBlackBorderPixel) // if the pixel is dark enough
  1790. {
  1791. // start a kill shadows search
  1792. searchPosition=position+step;
  1793. errors=0;
  1794. borderPixels=0;
  1795. for (y=1;y<SHADOW_HEIGHT;y++) // we don't expect a shadow to be more than SHADOW_HEIGHT pixels high
  1796. {
  1797. dr=(int)pBlurredBits[searchPosition]-r;
  1798. dg=(int)pBlurredBits[searchPosition+1]-g;
  1799. db=(int)pBlurredBits[searchPosition+2]-b;
  1800. r=(int)pBlurredBits[searchPosition];
  1801. g=(int)pBlurredBits[searchPosition+1];
  1802. b=(int)pBlurredBits[searchPosition+2];
  1803. if (dr<MAX_BLACK_BORDER_DELTA && dg<MAX_BLACK_BORDER_DELTA &&db<MAX_BLACK_BORDER_DELTA)
  1804. // only requirement is the intensity in each pixel must be less than the intensity of the previous pixel
  1805. // a shadow should be darkest at the edge of the image, not hte other way round
  1806. {
  1807. borderPixels++;
  1808. if (borderPixels>5)
  1809. break; // if we have found five pixels which meet the borderPixel specs, break;
  1810. }
  1811. else
  1812. {
  1813. errors++;
  1814. if (errors>3)
  1815. break; // if we recieve more than 3 errors, break
  1816. }
  1817. searchPosition+=step;
  1818. }
  1819. endPoint=y+5; // because of edge enhancement, we set the shadow width to be a bit more than it actually is
  1820. searchPosition+=2*step; // skip a couple of pixels because we may have missed the last couple of pixels of the shadow
  1821. nonBackgroundPixels=0;
  1822. backgroundPixels=0;
  1823. for (;y<20;y++) // we expect the next few pixels to be background pixels
  1824. {
  1825. r=(int)pOutputBits[searchPosition];
  1826. g=(int)pOutputBits[searchPosition+1];
  1827. b=(int)pOutputBits[searchPosition+2];
  1828. sourceR=(int)pBlurredBits[searchPosition];
  1829. sourceG=(int)pBlurredBits[searchPosition+1];
  1830. sourceB=(int)pBlurredBits[searchPosition+2];
  1831. if (r < MAX_KILL_SHADOW_BACKGROUND_APROXIMATION
  1832. && g < MAX_KILL_SHADOW_BACKGROUND_APROXIMATION
  1833. && b < MAX_KILL_SHADOW_BACKGROUND_APROXIMATION
  1834. // WARNING: commenting out the following 3 lines may greatly increases the number of innocent pixels that are deleted
  1835. && sourceR < MAX_KILL_SHADOW_BACKGROUND_UNEDITED
  1836. && sourceG < MAX_KILL_SHADOW_BACKGROUND_UNEDITED
  1837. && sourceB < MAX_KILL_SHADOW_BACKGROUND_UNEDITED
  1838. )
  1839. backgroundPixels++;
  1840. else
  1841. {
  1842. nonBackgroundPixels++;
  1843. }
  1844. if ((nonBackgroundPixels)>(backgroundPixels+4))
  1845. { // no way this is actually a shadow we are deleting
  1846. y=0;
  1847. break;
  1848. }
  1849. if (backgroundPixels>7) break;
  1850. searchPosition+=step;
  1851. }
  1852. // we only have a shadow if we get a number of dark pixels followed by a number light pixels
  1853. if (nonBackgroundPixels<3 && backgroundPixels>5 && borderPixels>errors && y!=0)
  1854. {
  1855. pShadowDepths[x]=MAX(pShadowDepths[x],endPoint);
  1856. }
  1857. }
  1858. // this is designed to kill a different kind of shadow, a light shadow far from any objects
  1859. // this code can be safely eliminated
  1860. //
  1861. r=pBlurredBits[position];
  1862. g=pBlurredBits[position+1];
  1863. b=pBlurredBits[position+2];
  1864. if (r>(minBlackBorderPixel/6)&&g>(minBlackBorderPixel/6)&&b>(minBlackBorderPixel/6))
  1865. {
  1866. searchPosition=position+step;
  1867. errors=0;
  1868. borderPixels=0;
  1869. for (y=1;y<11;y++)
  1870. {
  1871. dr=(int)pBlurredBits[searchPosition]-r;
  1872. dg=(int)pBlurredBits[searchPosition+1]-g;
  1873. db=(int)pBlurredBits[searchPosition+2]-b;
  1874. r=(int)pBlurredBits[searchPosition];
  1875. g=(int)pBlurredBits[searchPosition+1];
  1876. b=(int)pBlurredBits[searchPosition+2];
  1877. // much looser requirements for being a shadow
  1878. if (r>minBlackBorderPixel/7&&g>minBlackBorderPixel/7&&b>minBlackBorderPixel/7)
  1879. {
  1880. borderPixels++;
  1881. }
  1882. else
  1883. {
  1884. errors++;
  1885. }
  1886. searchPosition+=step;
  1887. }
  1888. endPoint=y-3;
  1889. searchPosition+=5*step;
  1890. nonBackgroundPixels=0;
  1891. backgroundPixels=0;
  1892. for (;y<35;y++)
  1893. {
  1894. r=(int)pOutputBits[searchPosition];
  1895. g=(int)pOutputBits[searchPosition+1];
  1896. b=(int)pOutputBits[searchPosition+2];
  1897. sourceR=(int)pBlurredBits[searchPosition];
  1898. sourceG=(int)pBlurredBits[searchPosition+1];
  1899. sourceB=(int)pBlurredBits[searchPosition+2];
  1900. // much stricter requirements for being a background pixel
  1901. // with these stricter requirements, we are almost guaranteed not to eliminate any
  1902. // valid pixels while searching for black borders
  1903. // the idea is balancing looser requirements in one area with stricter requirements in another
  1904. if (r < MAX_KILL_SHADOW_BACKGROUND_APROXIMATION/29
  1905. && g < MAX_KILL_SHADOW_BACKGROUND_APROXIMATION/29
  1906. && b < MAX_KILL_SHADOW_BACKGROUND_APROXIMATION/29
  1907. && sourceR < MAX_KILL_SHADOW_BACKGROUND_UNEDITED/39
  1908. && sourceG < MAX_KILL_SHADOW_BACKGROUND_UNEDITED/39
  1909. && sourceB < MAX_KILL_SHADOW_BACKGROUND_UNEDITED/39
  1910. )
  1911. backgroundPixels++;
  1912. else
  1913. {
  1914. nonBackgroundPixels++;
  1915. break;
  1916. }
  1917. searchPosition+=step;
  1918. }
  1919. if (nonBackgroundPixels==0) // the pixel isn't a shadow pixel unless all of the backgroundPixels tested were background pixels
  1920. {
  1921. pShadowDepths[x]=MAX(pShadowDepths[x],endPoint); // update the shadowDepth for the pixel
  1922. // corners can be very problematic
  1923. // because this algorithm will by definition fail on any corner line
  1924. // so we cheat...
  1925. if (x<CORNER_WIDTH)
  1926. {
  1927. for (i=0;i<CORNER_WIDTH;i++)
  1928. {
  1929. pShadowDepths[i]=MAX(pShadowDepths[i],endPoint);
  1930. }
  1931. }
  1932. if (x+CORNER_WIDTH>width)
  1933. {
  1934. for (i=width-CORNER_WIDTH;i<width;i++)
  1935. {
  1936. pShadowDepths[i]=MAX(pShadowDepths[i],endPoint);
  1937. }
  1938. }
  1939. }
  1940. }
  1941. // this is designed to kill a different kind of shadow, a small light shadow close to objects
  1942. // this code can be safely eliminated
  1943. // it was mainly written simply to explore the problem space of border elimination
  1944. //
  1945. // if this code is saved beyond a couple of test runs, we will need to turn some of its constants into real constants
  1946. // it seems from prelim tests that this code may be preferable to the previous test function
  1947. {
  1948. searchPosition=position+step;
  1949. errors=0;
  1950. borderPixels=0;
  1951. nonBackgroundPixels=0;
  1952. backgroundPixels=0;
  1953. longestBackgroundPixelString=0;
  1954. endPoint=0;
  1955. // we don't bother with looking for a string of black pixels in this case
  1956. // which is probably more intelegent than the previous code blocks
  1957. // instead we simply look for long strings of background pixels
  1958. // while at the same time, terminating the search of we come across too many non-background pixels
  1959. for (y=0;y<16;y++)
  1960. {
  1961. r=(int)pOutputBits[searchPosition];
  1962. g=(int)pOutputBits[searchPosition+1];
  1963. b=(int)pOutputBits[searchPosition+2];
  1964. sourceR=(int)pBlurredBits[searchPosition];
  1965. sourceG=(int)pBlurredBits[searchPosition+1];
  1966. sourceB=(int)pBlurredBits[searchPosition+2];
  1967. if (r < 24
  1968. && g < 24
  1969. && b < 24
  1970. && sourceR < 12
  1971. && sourceG < 12
  1972. && sourceB < 12
  1973. )
  1974. backgroundPixels++;
  1975. else
  1976. {
  1977. if (y>5) nonBackgroundPixels++;
  1978. if (backgroundPixels>longestBackgroundPixelString)
  1979. {
  1980. endPoint=y;
  1981. longestBackgroundPixelString=backgroundPixels;
  1982. }
  1983. backgroundPixels=0;
  1984. if (nonBackgroundPixels>1) break;
  1985. }
  1986. searchPosition+=step;
  1987. }
  1988. if (backgroundPixels>longestBackgroundPixelString) // was the longestBackgroundPixelString the last?
  1989. {
  1990. longestBackgroundPixelString=backgroundPixels;
  1991. endPoint=16;
  1992. }
  1993. if (longestBackgroundPixelString>6)
  1994. {
  1995. pShadowDepths[x]=MAX(pShadowDepths[x],endPoint-4);
  1996. // corners can be problematic
  1997. // because this algorithm will by definition fail on a black corner
  1998. // so we cheat...
  1999. if (x<CORNER_WIDTH)
  2000. {
  2001. for (i=0;i<CORNER_WIDTH;i++)
  2002. {
  2003. pShadowDepths[i]=MAX(pShadowDepths[i],endPoint);
  2004. }
  2005. }
  2006. if (x+CORNER_WIDTH>width)
  2007. {
  2008. for (i=width-CORNER_WIDTH;i<width;i++)
  2009. {
  2010. pShadowDepths[i]=MAX(pShadowDepths[i],endPoint);
  2011. }
  2012. }
  2013. }
  2014. }
  2015. position+=dx*4; // increment the position by 1 unit to go to the next row
  2016. }
  2017. for (x=0;x<width;x++)
  2018. {
  2019. pTempShadowDepths[x]=pShadowDepths[x];
  2020. }
  2021. if (SMOOTH_BORDER) // shadows don't just come out of nowhere, if row x has a depth 20 shadow, its likely that we made a mistake and pixel x-1 also has a depth 20 shadow
  2022. {
  2023. for (x=2;x<width-2;x++)
  2024. {
  2025. pTempShadowDepths[x]=MAX(pTempShadowDepths[x],pShadowDepths[x-1]);
  2026. pTempShadowDepths[x]=MAX(pTempShadowDepths[x],pShadowDepths[x+1]);
  2027. pTempShadowDepths[x]=MAX(pTempShadowDepths[x],pShadowDepths[x-2]);
  2028. pTempShadowDepths[x]=MAX(pTempShadowDepths[x],pShadowDepths[x+2]);
  2029. }
  2030. }
  2031. // now remove the black border
  2032. // we loop through all rows x and then eliminate the first pTempShadowDepths[x] pixels in that row
  2033. position=startPosition;
  2034. step=dy;
  2035. for (x=0;x<width;x++)
  2036. {
  2037. newPosition=position;
  2038. for (y=0;y<pTempShadowDepths[x];y++)
  2039. {
  2040. pBitmapPixels[newPosition]=DEAD_PIXEL; // set each shadow to be a dead pixel
  2041. // dead pixels are the only pixels not vulnerable to edge enhancements...
  2042. // important if we do any KillShadows edge enhancement passes after calling kill black border
  2043. if (VISUAL_DEBUG)
  2044. pDebugPixels[newPosition]=((pDebugPixels[newPosition] & 0xfefefe)>>1)+((DEBUGCOLOR& 0xfefefe)>>1);
  2045. newPosition+=step;
  2046. }
  2047. position+=dx;
  2048. }
  2049. }
  2050. // clean up our memory
  2051. delete[] pTempShadowDepths;
  2052. delete[] pShadowDepths;
  2053. }
  2054. }
  2055. // dib manipulation functions
  2056. // the following are dib wrapper functions stolen and then modified... from utils.cpp
  2057. // these functions are now obsolete and only used in region debug mode
  2058. // where we need to load bitmaps from files
  2059. /*******************************************************************************
  2060. *
  2061. * SetBMI
  2062. *
  2063. * DESCRIPTION:
  2064. * Setup bitmap info.
  2065. *
  2066. * PARAMETERS:
  2067. *
  2068. *******************************************************************************/
  2069. void SetBMI( PBITMAPINFO pbmi, LONG width, LONG height, LONG depth)
  2070. {
  2071. pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  2072. pbmi->bmiHeader.biWidth = width;
  2073. pbmi->bmiHeader.biHeight = height;
  2074. pbmi->bmiHeader.biPlanes = 1;
  2075. pbmi->bmiHeader.biBitCount = (WORD) depth;
  2076. pbmi->bmiHeader.biCompression = BI_RGB;
  2077. pbmi->bmiHeader.biSizeImage = 0;
  2078. pbmi->bmiHeader.biXPelsPerMeter = 0;
  2079. pbmi->bmiHeader.biYPelsPerMeter = 0;
  2080. pbmi->bmiHeader.biClrUsed = 0;
  2081. pbmi->bmiHeader.biClrImportant = 0;
  2082. }
  2083. /*******************************************************************************
  2084. *
  2085. * AllocDibFileFromBits
  2086. *
  2087. * DESCRIPTION:
  2088. * Given an unaligned bits buffer, allocate a buffer lager enough to hold the
  2089. * DWORD aligned DIB file and fill it in.
  2090. *
  2091. * PARAMETERS:
  2092. *
  2093. *******************************************************************************/
  2094. PBYTE AllocDibFileFromBits( PBYTE pBits, UINT width, UINT height, UINT depth)
  2095. {
  2096. PBYTE pdib;
  2097. UINT uiScanLineWidth, uiSrcScanLineWidth, cbDibSize;
  2098. int bitsSize;
  2099. // Align scanline to ULONG boundary
  2100. uiSrcScanLineWidth = (width * depth) / 8;
  2101. uiScanLineWidth = (uiSrcScanLineWidth + 3) & 0xfffffffc;
  2102. // DEBUG:
  2103. // uiSrcScanLineWidth=uiScanLineWidth;
  2104. // Calculate DIB size and allocate memory for the DIB.
  2105. bitsSize=height * uiScanLineWidth;
  2106. cbDibSize = bitsSize+sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFO);
  2107. pdib = (PBYTE) LocalAlloc(LMEM_FIXED, cbDibSize);
  2108. if (pdib)
  2109. {
  2110. PBITMAPFILEHEADER pbmfh = (PBITMAPFILEHEADER)pdib;
  2111. PBITMAPINFO pbmi = (PBITMAPINFO)(pdib + sizeof(BITMAPFILEHEADER));
  2112. PBYTE pb = (PBYTE)pbmi+ sizeof(BITMAPINFO);
  2113. // Setup bitmap file header.
  2114. pbmfh->bfType = 'MB';
  2115. pbmfh->bfSize = cbDibSize;
  2116. pbmfh->bfOffBits = static_cast<DWORD>(pb - pdib);
  2117. // Setup bitmap info.
  2118. SetBMI(pbmi,width, height, depth);
  2119. // WIA_TRACE(("AllocDibFileFromBits, uiScanLineWidth: %d, pdib: 0x%08X, pbmi: 0x%08X, pbits: 0x%08X", uiScanLineWidth, pdib, pbmi, pb));
  2120. // Copy the bits.
  2121. pb-=3;
  2122. pBits-=3; // BUG FIX BECAUSE THE PERSON WHO WROTE THIS COULDN'T KEEP THEIR BITS STRAIGHT
  2123. memcpy(pb, pBits, bitsSize);
  2124. }
  2125. else
  2126. {
  2127. // WIA_ERROR(("AllocDibFileFromBits, LocalAlloc of %d bytes failed", cbDibSize));
  2128. }
  2129. return(pdib);
  2130. }
  2131. /*******************************************************************************
  2132. *
  2133. * DIBBufferToBMP
  2134. *
  2135. * DESCRIPTION:
  2136. * Make a BMP object from a DWORD aligned DIB file memory buffer
  2137. *
  2138. * PARAMETERS:
  2139. *
  2140. *******************************************************************************/
  2141. HBITMAP DIBBufferToBMP(HDC hDC, PBYTE pDib, BOOLEAN bFlip)
  2142. {
  2143. HBITMAP hBmp = NULL;
  2144. PBITMAPINFO pbmi = (BITMAPINFO*)(pDib);
  2145. PBYTE pBits = pDib + GetBmiSize(pbmi);
  2146. if (bFlip)
  2147. {
  2148. pbmi->bmiHeader.biHeight = -pbmi->bmiHeader.biHeight;
  2149. }
  2150. hBmp = CreateDIBitmap(hDC, &pbmi->bmiHeader, CBM_INIT, pBits, pbmi, DIB_RGB_COLORS);
  2151. if (!hBmp)
  2152. {
  2153. ;//WIA_ERROR(("DIBBufferToBMP, CreateDIBitmap failed %d", GetLastError(void)));
  2154. }
  2155. return(hBmp);
  2156. }
  2157. /*******************************************************************************
  2158. *
  2159. * ReadDIBFile
  2160. *
  2161. * DESCRIPTION:
  2162. *
  2163. * PARAMETERS:
  2164. *
  2165. *******************************************************************************/
  2166. HRESULT ReadDIBFile(LPTSTR pszFileName, PBYTE *ppDib)
  2167. {
  2168. HRESULT hr = S_FALSE;
  2169. HANDLE hFile, hMap;
  2170. PBYTE pFile, pBits;
  2171. *ppDib = NULL;
  2172. hFile = CreateFile(pszFileName,
  2173. GENERIC_WRITE | GENERIC_READ,
  2174. FILE_SHARE_WRITE,
  2175. NULL,
  2176. OPEN_EXISTING,
  2177. FILE_ATTRIBUTE_NORMAL,
  2178. NULL);
  2179. if (hFile == INVALID_HANDLE_VALUE)
  2180. {
  2181. //WIA_ERROR(("ReadDIBFile, unable to open %s", pszFileName));
  2182. return(hr);
  2183. }
  2184. hMap = CreateFileMapping(hFile,
  2185. NULL,
  2186. PAGE_READWRITE,
  2187. 0,
  2188. 0,
  2189. NULL);
  2190. if (!hMap)
  2191. {
  2192. //WIA_ERROR(("ReadDIBFile, CreateFileMapping failed"));
  2193. goto close_hfile_exit;
  2194. }
  2195. pFile = (PBYTE)MapViewOfFileEx(hMap,
  2196. FILE_MAP_READ | FILE_MAP_WRITE,
  2197. 0,
  2198. 0,
  2199. 0,
  2200. NULL);
  2201. if (pFile)
  2202. {
  2203. PBITMAPFILEHEADER pbmFile = (PBITMAPFILEHEADER)pFile;
  2204. PBITMAPINFO pbmi = (PBITMAPINFO)(pFile + sizeof(BITMAPFILEHEADER));
  2205. // validate bitmap
  2206. if (pbmFile->bfType == 'MB')
  2207. {
  2208. // Calculate color table size.
  2209. LONG bmiSize, ColorMapSize = 0;
  2210. if (pbmi->bmiHeader.biBitCount == 1)
  2211. {
  2212. ColorMapSize = 2 - 1;
  2213. }
  2214. else if (pbmi->bmiHeader.biBitCount == 4)
  2215. {
  2216. ColorMapSize = 16 - 1;
  2217. }
  2218. else if (pbmi->bmiHeader.biBitCount == 8)
  2219. {
  2220. ColorMapSize = 256 - 1;
  2221. }
  2222. bmiSize = sizeof(BITMAPINFO) + sizeof(RGBQUAD) * ColorMapSize;
  2223. pBits = pFile + sizeof(BITMAPFILEHEADER) + bmiSize;
  2224. *ppDib = AllocDibFileFromBits(pBits,
  2225. pbmi->bmiHeader.biWidth,
  2226. pbmi->bmiHeader.biHeight,
  2227. pbmi->bmiHeader.biBitCount);
  2228. if (*ppDib)
  2229. {
  2230. hr = S_OK;
  2231. }
  2232. }
  2233. else
  2234. {
  2235. //WIA_ERROR(("ReadDIBFile, %s is not a valid bitmap file", pszFileName));
  2236. }
  2237. }
  2238. else
  2239. {
  2240. //WIA_ERROR(("ReadDIBFile, MapViewOfFileEx failed"));
  2241. goto close_hmap_exit;
  2242. }
  2243. UnmapViewOfFile(pFile);
  2244. close_hmap_exit:
  2245. CloseHandle(hMap);
  2246. close_hfile_exit:
  2247. CloseHandle(hFile);
  2248. return(hr);
  2249. }
  2250. /*******************************************************************************
  2251. *
  2252. * GetBmiSize
  2253. *
  2254. * DESCRIPTION:
  2255. * Should never get biCompression == BI_RLE.
  2256. *
  2257. * PARAMETERS:
  2258. *
  2259. *******************************************************************************/
  2260. LONG GetBmiSize(PBITMAPINFO pbmi)
  2261. {
  2262. // determine the size of bitmapinfo
  2263. LONG lSize = pbmi->bmiHeader.biSize;
  2264. // no color table cases
  2265. if (
  2266. (pbmi->bmiHeader.biBitCount == 24) ||
  2267. ((pbmi->bmiHeader.biBitCount == 32) &&
  2268. (pbmi->bmiHeader.biCompression == BI_RGB)))
  2269. {
  2270. // no colors unless stated
  2271. lSize += sizeof(RGBQUAD) * pbmi->bmiHeader.biClrUsed;
  2272. return(lSize);
  2273. }
  2274. // bitfields cases
  2275. if (((pbmi->bmiHeader.biBitCount == 32) &&
  2276. (pbmi->bmiHeader.biCompression == BI_BITFIELDS)) ||
  2277. (pbmi->bmiHeader.biBitCount == 16))
  2278. {
  2279. lSize += 3 * sizeof(RGBQUAD);
  2280. return(lSize);
  2281. }
  2282. // palette cases
  2283. if (pbmi->bmiHeader.biBitCount == 1)
  2284. {
  2285. LONG lPal = pbmi->bmiHeader.biClrUsed;
  2286. if ((lPal == 0) || (lPal > 2))
  2287. {
  2288. lPal = 2;
  2289. }
  2290. lSize += lPal * sizeof(RGBQUAD);
  2291. return(lSize);
  2292. }
  2293. // palette cases
  2294. if (pbmi->bmiHeader.biBitCount == 4)
  2295. {
  2296. LONG lPal = pbmi->bmiHeader.biClrUsed;
  2297. if ((lPal == 0) || (lPal > 16))
  2298. {
  2299. lPal = 16;
  2300. }
  2301. lSize += lPal * sizeof(RGBQUAD);
  2302. return(lSize);
  2303. }
  2304. // palette cases
  2305. if (pbmi->bmiHeader.biBitCount == 8)
  2306. {
  2307. LONG lPal = pbmi->bmiHeader.biClrUsed;
  2308. if ((lPal == 0) || (lPal > 256))
  2309. {
  2310. lPal = 256;
  2311. }
  2312. lSize += lPal * sizeof(RGBQUAD);
  2313. return(lSize);
  2314. }
  2315. // error
  2316. return(0);
  2317. }
  2318. INT GetColorTableSize (UINT uBitCount, UINT uCompression)
  2319. {
  2320. INT nSize;
  2321. switch (uBitCount)
  2322. {
  2323. case 32:
  2324. if (uCompression != BI_BITFIELDS)
  2325. {
  2326. nSize = 0;
  2327. break;
  2328. }
  2329. // fall through
  2330. case 16:
  2331. nSize = 3 * sizeof(DWORD);
  2332. break;
  2333. case 24:
  2334. nSize = 0;
  2335. break;
  2336. default:
  2337. nSize = ((UINT)1 << uBitCount) * sizeof(RGBQUAD);
  2338. break;
  2339. }
  2340. return(nSize);
  2341. }
  2342. DWORD CalcBitsSize (UINT uWidth, UINT uHeight, UINT uBitCount, UINT uPlanes, int nAlign)
  2343. {
  2344. int nAWidth,nHeight,nABits;
  2345. DWORD dwSize;
  2346. nABits = (nAlign << 3);
  2347. nAWidth = nABits-1;
  2348. //
  2349. // Determine the size of the bitmap based on the (nAlign) size. Convert
  2350. // this to size-in-bytes.
  2351. //
  2352. nHeight = uHeight * uPlanes;
  2353. dwSize = (DWORD)(((uWidth * uBitCount) + nAWidth) / nABits) * nHeight;
  2354. dwSize = dwSize * nAlign;
  2355. return(dwSize);
  2356. }
  2357. //
  2358. // Converts hBitmap to a DIB
  2359. //
  2360. HGLOBAL BitmapToDIB (HDC hdc, HBITMAP hBitmap)
  2361. {
  2362. BITMAP bm = {0};
  2363. HANDLE hDib;
  2364. PBYTE lpDib,lpBits;
  2365. DWORD dwLength;
  2366. DWORD dwBits;
  2367. UINT uColorTable;
  2368. INT iNeedMore;
  2369. BOOL bDone;
  2370. INT nBitCount;
  2371. //
  2372. // Get the size of the bitmap. These values are used to setup the memory
  2373. // requirements for the DIB.
  2374. //
  2375. if (GetObject(hBitmap,sizeof(BITMAP),reinterpret_cast<PVOID>(&bm)))
  2376. {
  2377. nBitCount = bm.bmBitsPixel * bm.bmPlanes;
  2378. uColorTable = GetColorTableSize((UINT)nBitCount, BI_RGB);
  2379. dwBits = CalcBitsSize(bm.bmWidth,bm.bmHeight,nBitCount,1,sizeof(DWORD));
  2380. do
  2381. {
  2382. bDone = TRUE;
  2383. dwLength = dwBits + sizeof(BITMAPINFOHEADER) + uColorTable;
  2384. // Create the DIB. First, to the size of the bitmap.
  2385. //
  2386. if (hDib = GlobalAlloc(GHND,dwLength))
  2387. {
  2388. if (lpDib = reinterpret_cast<PBYTE>(GlobalLock(hDib)))
  2389. {
  2390. ((LPBITMAPINFOHEADER)lpDib)->biSize = sizeof(BITMAPINFOHEADER);
  2391. ((LPBITMAPINFOHEADER)lpDib)->biWidth = (DWORD)bm.bmWidth;
  2392. ((LPBITMAPINFOHEADER)lpDib)->biHeight = (DWORD)bm.bmHeight;
  2393. ((LPBITMAPINFOHEADER)lpDib)->biPlanes = 1;
  2394. ((LPBITMAPINFOHEADER)lpDib)->biBitCount = (WORD)nBitCount;
  2395. ((LPBITMAPINFOHEADER)lpDib)->biCompression = 0;
  2396. ((LPBITMAPINFOHEADER)lpDib)->biSizeImage = 0;
  2397. ((LPBITMAPINFOHEADER)lpDib)->biXPelsPerMeter = 0;
  2398. ((LPBITMAPINFOHEADER)lpDib)->biYPelsPerMeter = 0;
  2399. ((LPBITMAPINFOHEADER)lpDib)->biClrUsed = 0;
  2400. ((LPBITMAPINFOHEADER)lpDib)->biClrImportant = 0;
  2401. // Get the size of the bitmap.
  2402. // The biSizeImage contains the bytes
  2403. // necessary to store the DIB.
  2404. //
  2405. GetDIBits(hdc,hBitmap,0,bm.bmHeight,NULL,(LPBITMAPINFO)lpDib,DIB_RGB_COLORS);
  2406. iNeedMore = ((LPBITMAPINFOHEADER)lpDib)->biSizeImage - dwBits;
  2407. if (iNeedMore > 0)
  2408. {
  2409. dwBits = dwBits + (((iNeedMore + 3) / 4)*4);
  2410. bDone = FALSE;
  2411. }
  2412. else
  2413. {
  2414. lpBits = lpDib+sizeof(BITMAPINFOHEADER)+uColorTable;
  2415. GetDIBits(hdc,hBitmap,0,bm.bmHeight,lpBits,(LPBITMAPINFO)lpDib,DIB_RGB_COLORS);
  2416. GlobalUnlock(hDib);
  2417. return(hDib);
  2418. }
  2419. GlobalUnlock(hDib);
  2420. }
  2421. GlobalFree(hDib);
  2422. }
  2423. }
  2424. while (!bDone);
  2425. }
  2426. return(NULL);
  2427. }