📄 dibdoc.cpp
字号:
// DIBDoc.cpp : implementation file
//
#include "stdafx.h"
#include "Segment.h"
#include "DIBDoc.h"
#include "MonochromeBitmap.h"
#include "Line.h"
#include "HistogramDlg.h"
#include <vector>
#include <algorithm>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//-----------------------------------------------------------------
//
// CDIBMonochromeBitmap - this forms the base class of the monochrome
// bitmaps that extract their rasters from
// device independent bitmaps
//
//-----------------------------------------------------------------
class CDIBMonochromeBitmap: public CMonochromeBitmap
{
protected:
CDIBDoc *m_pDoc;
public:
CDIBMonochromeBitmap() { m_pDoc = NULL; }
void SetDoc(CDIBDoc &doc) {
m_pDoc = &doc;
}
virtual int Width() const {
ASSERT(m_pDoc);
return m_pDoc->GetBitmapInfo()->bmiHeader.biWidth;
}
virtual int Height() const {
ASSERT(m_pDoc);
return m_pDoc->GetBitmapInfo()->bmiHeader.biHeight;
}
};
class CDIB1MonochromeBitmap: public CDIBMonochromeBitmap
{
public:
const BYTE *GetRaster(int nLine);
};
const BYTE *CDIB1MonochromeBitmap::GetRaster(int nLine)
{
ASSERT(m_pDoc);
return m_pDoc->GetRaster(nLine);
}
//-----------------------------------------------------------------
//
// CDIB4MonochromeBitmap - this is a monochrome bitmap that extracts
// its rasters from a 16 color DIB.
//
// NOTE: This class really hasn't been tested.
//-----------------------------------------------------------------
class CDIB4MonochromeBitmap: public CDIBMonochromeBitmap
{
public:
CDIB4MonochromeBitmap();
virtual ~CDIB4MonochromeBitmap();
const BYTE *GetRaster(int nLine);
protected:
BYTE *m_pRaster;
};
CDIB4MonochromeBitmap::CDIB4MonochromeBitmap()
{
m_pRaster = NULL;
}
CDIB4MonochromeBitmap::~CDIB4MonochromeBitmap()
{
delete [] m_pRaster;
}
const BYTE *CDIB4MonochromeBitmap::GetRaster(int nLine)
{
if (m_pRaster == NULL) {
ASSERT(m_pDoc);
m_pRaster = new BYTE [m_pDoc->GetRasterWidthInBytes()];
}
const CByteArray &bThreshold = m_pDoc->GetThresholdTable();
int nLimit = Width();
const BYTE *pSrc = m_pDoc->GetRaster(nLine);
BYTE *pDest = m_pRaster;
for (int i = 0; i < nLimit; i += 8) {
BYTE b = 0;
if (bThreshold[*pSrc & 15]) {
b = 0x80;
}
if (bThreshold[*pSrc++ >> 4]) {
b += 0x40;
}
if (bThreshold[*pSrc & 15]) {
b += 0x20;
}
if (bThreshold[*pSrc++ >> 4]) {
b += 0x10;
}
if (bThreshold[*pSrc & 15]) {
b += 0x08;
}
if (bThreshold[*pSrc++ >> 4]) {
b += 0x04;
}
if (bThreshold[*pSrc & 15]) {
b += 0x02;
}
if (bThreshold[*pSrc++ >> 4]) {
b += 0x01;
}
*pDest++ = b;
}
return m_pRaster;
}
//-----------------------------------------------------------------
//
// CDIB8MonochromeBitmap - this one is the 256 color bitmap, the one
// most frequently used to store pictures and gray
// scales.
//
//-----------------------------------------------------------------
class CDIB8MonochromeBitmap: public CDIBMonochromeBitmap
{
public:
virtual void GetBrightnessHistogram(vector<int> &a,vector<int> &vBrightness);
virtual ~CDIB8MonochromeBitmap();
CDIB8MonochromeBitmap();
virtual const BYTE *GetRaster(int nLine);
virtual void BuildBlobList();
protected:
CByteArray m_bCachedRaster;
BYTE * m_pCachedRaster;
};
//-----------------------------------------------------------------
//
// CDIB8MonochromeBitmap::CDIB8MonochromeBitmap
//
//-----------------------------------------------------------------
CDIB8MonochromeBitmap::CDIB8MonochromeBitmap()
{
}
//-----------------------------------------------------------------
//
// CDIB8MonochromeBitmap::~CDIB8MonochromeBitmap
//
//-----------------------------------------------------------------
CDIB8MonochromeBitmap::~CDIB8MonochromeBitmap()
{
}
//-----------------------------------------------------------------
//
// CDIB8MonochromeBitmap::GetBrightnessHistogram - This creates
// a histogram whose elements indicate the relative
// number of pixels at each brightness. You can use
// this to determine where to threshold.
//
// a - Histogram of # of pixels at each index. Indices are in order of
// lowest to highest brightness.
// aBrightness - Each element of this array contains the index into
// the histogram of the indexed element.
//-----------------------------------------------------------------
void CDIB8MonochromeBitmap::GetBrightnessHistogram(vector<int> & a,vector<int> &aBrightness)
{
// Here, we sample a bunch of random points to get an idea
// of the brightness distribution.
int nColors = m_pDoc->GetBitmapInfo()->bmiHeader.biClrUsed;
if (nColors == 0) nColors = 256;
RGBQUAD *pColors = m_pDoc->GetBitmapInfo()->bmiColors;
a.resize(nColors);
aBrightness.resize(nColors);
for (int i = 0; i < nColors; i++) a[i] = 0;
// We do a little trick here. We put the brightness in
// bits 8+ of an int and the index in the low 7 bits.
// We sort the array and we get the indices ordered by
// brightness.
vector<int> vBrightness;
vBrightness.resize(nColors);
for (i = 0; i < nColors; i++) {
int nBrightness = pColors[i].rgbRed + pColors[i].rgbBlue + pColors[i].rgbGreen;
vBrightness[i] = i + (nBrightness << 8);
}
sort(vBrightness.begin(), &vBrightness[vBrightness.size()]);
// Now, we put the index in the top byte and put the ranking in the
// bottom and sort. The "i"th index will contain the ranking of
// the indexed color.
for (i = 0; i < nColors; i++) {
vBrightness[i] = ((vBrightness[i] & 0xFF) << 8) + i;
}
sort(vBrightness.begin(), &vBrightness[vBrightness.size()]);
for (i = 0; i < nColors; i++) {
vBrightness[i] &= 0xFF;
}
// Let's record the brightnesses in "aBrightness"
for (i = 0; i < nColors; i++) {
RGBQUAD *pClr = pColors + vBrightness[i];
aBrightness[i] = pClr->rgbRed + pClr->rgbBlue + pClr->rgbGreen;
}
const int nWidth = Width();
const int nHeight = Height();
for (i = 0; i < 100; i++) {
int nY = MulDiv(i,nHeight - 1,100);
for (int j = 0; j < 100; j++) {
int nX = MulDiv(j,nWidth - 1,100);
BYTE bValue = m_pDoc->GetRaster(nY)[nX];
ASSERT(vBrightness[bValue] >= 0);
ASSERT(vBrightness[bValue] < nColors);
a[vBrightness[bValue]]++;
}
}
}
//-----------------------------------------------------------------
//
// CDIB8MonochromeBitmap::GetRaster - return a pointer to a thresholded
// monochrome raster (1 bit per pixel) for the indexed
// line. NOTE: only one line is cached at a time, sorry.
//
// nLine - Y index of bitmap line to fetch.
//-----------------------------------------------------------------
const BYTE *CDIB8MonochromeBitmap::GetRaster(int nLine)
{
if (m_bCachedRaster.GetSize() < Width()) {
int nMonochromeWidth = m_pDoc->GetRasterWidthInBytes();
m_bCachedRaster.SetSize(nMonochromeWidth);
m_pCachedRaster = m_bCachedRaster.GetData();
memset(m_pCachedRaster,0,nMonochromeWidth);
}
const CByteArray &m_bThreshold = m_pDoc->GetThresholdTable();
BYTE *pDest = m_pCachedRaster;
const BYTE *pSrc = m_pDoc->GetRaster(nLine);
// We do 8 bit chunks here. We'll handle the end boundary
// condition separately
const int nWidth = Width() & ~ 7;
BYTE b;
for (int i = 0; i < nWidth; i += 8) {
if (m_bThreshold[*pSrc++]) b = 0x80; else b = 0;
if (m_bThreshold[*pSrc++]) b += 0x40;
if (m_bThreshold[*pSrc++]) b += 0x20;
if (m_bThreshold[*pSrc++]) b += 0x10;
if (m_bThreshold[*pSrc++]) b += 0x08;
if (m_bThreshold[*pSrc++]) b += 0x04;
if (m_bThreshold[*pSrc++]) b += 0x02;
if (m_bThreshold[*pSrc++]) b += 0x01;
*pDest++ = b;
}
if (nWidth != Width()) {
b = 0;
BYTE bBit = 0x80;
while(i++ < Width()) {
if (m_bThreshold[*pSrc++]) b += bBit;
bBit = bBit >> 1;
}
*pDest = b;
}
return m_pCachedRaster;
}
//-----------------------------------------------------------------
//
// CDIB8MonochromeBitmap::BuildBlobList - I've just duplicated the code
// pretty much for the x8 bitmap
// in order to make it go fast.
//
//-----------------------------------------------------------------
void CDIB8MonochromeBitmap::BuildBlobList()
{
const CByteArray &bThresholdTable = m_pDoc->GetThresholdTable();
CLineVectorIterator clviNew,clviEnd;
AllocateLineBlock(clviNew,clviEnd);
m_pBlob.reserve(eBlobsPerBlock);
// Create two vectors of pointers to lines to hold the new line list
// and the old line list. We initialize them to hold Width()/2 members.
// This is the maximum possible number of lines.
CLinePointerVector clpvRaster[2];
clpvRaster[0].reserve(Width()/2);
clpvRaster[1].reserve(Width()/2);
int nOldLineIndex = 0;
int nNewLineIndex = 1;
// At the start, the old line list has no members.
CLinePointerVectorIterator clpviOldEnd = clpvRaster[nOldLineIndex].begin();
for (int nY = 0; nY < Height(); nY++) {
CLinePointerVector &clpvOld = clpvRaster[nOldLineIndex];
CLinePointerVector &clpvNew = clpvRaster[nNewLineIndex];
nOldLineIndex = nOldLineIndex ^ 1;
nNewLineIndex = nNewLineIndex ^ 1;
CLinePointerVectorIterator clpviOld = clpvOld.begin();
CLine *pLineOld;
if (clpviOld == clpviOldEnd) {
pLineOld = NULL;
} else {
pLineOld = *clpviOld++;
}
CLinePointerVectorIterator clpviNew = clpvNew.begin();
// Get the raster to process and set up for the first byte
const BYTE *pRaster = m_pDoc->GetRaster(nY);
// Set up the X extents.
int nX = 0;
const int nXEnd = Width();
while(nX < nXEnd) {
CBlob *pBlobOld;
// Search for the start of a line.
while(bThresholdTable[*pRaster++] == 0) {
if (++nX == nXEnd) goto linedone;
}
// Start a line.
if (clviNew == clviEnd) {
// need more memory for lines.
AllocateLineBlock(clviNew,clviEnd);
}
CLine &lineNew = *clviNew++;
lineNew.m_nXStart = nX;
lineNew.m_pNextLineSameBlob = 0;
lineNew.m_nY = nY;
// Put it on the new list.
*clpviNew++ = &lineNew;
// Find the extent of the line.
nX++;
while (nX != nXEnd) {
if (bThresholdTable[*pRaster++] == 0) break;
nX++;
}
lineNew.m_nXEnd = nX++;
// Now finish all old lines wholly before our new line
if (pLineOld) {
while (pLineOld->m_nXEnd < lineNew.m_nXStart) {
pBlobOld = pLineOld->m_pBlob;
if (--(pBlobOld->m_nOpenLines) == 0) {
AddBlob(pBlobOld);
}
if (clpviOld == clpviOldEnd) {
pLineOld = NULL;
break;
} else {
pLineOld = *clpviOld++;
}
}
}
// Do the first line that overlaps our new line
if (pLineOld && pLineOld->m_nXStart <= lineNew.m_nXEnd) {
pBlobOld = pLineOld->m_pBlob;
lineNew.m_pBlob = pBlobOld;
*(pBlobOld->m_ppLastLine) = &lineNew;
pBlobOld->m_ppLastLine = &(lineNew.m_pNextLineSameBlob);
// See if the new line ends the old line or vice-versa.
if (pLineOld->m_nXEnd > lineNew.m_nXEnd) {
// the old line extends past the new line.
// the new line gives the old line's blob another open line.
pBlobOld->m_nOpenLines++;
continue;
} else {
// we close the old line and extend the blob at the
// same time. We continue to close old lines.
if (clpviOld == clpviOldEnd) {
pLineOld = NULL;
} else {
pLineOld = *clpviOld++;
while(pLineOld->m_nXEnd <= lineNew.m_nXEnd) {
// End this line too. We have two cases:
CBlob *pBlobOther;
if ((pBlobOther = pLineOld->m_pBlob) == pBlobOld) {
// the old line is part of the new line's blob.
// This is just a loop.
pBlobOld->m_nLoops++;
pBlobOld->m_nOpenLines--;
} else {
// This is the merge case
pBlobOld->Merge(pBlobOther);
delete pBlobOther;
pBlobOld->m_nOpenLines--;
}
if (clpviOld == clpviOldEnd) {
pLineOld = NULL;
break;
} else {
pLineOld = *clpviOld++;
}
}
if (pLineOld && pLineOld->m_nXStart <= lineNew.m_nXEnd) {
// This last old line overlaps and ends the new line.
CBlob *pBlobOther = pLineOld->m_pBlob;
if (pBlobOther == pBlobOld) {
pBlobOld->m_nLoops++;
} else {
pBlobOld->Merge(pBlobOther);
delete pBlobOther;
}
}
}
}
} else {
// This is the case where no line overlaps the new line.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -