Многопоточное программирование на C++
Реферат, 24 Января 2012, автор: пользователь скрыл имя
Краткое описание
Для начала поговорим о подводных камнях C++ применительно к MT программированию. На первый взгляд может показаться, что написанные на C++ потоки, не изменяющие одни и те же данные одновременно, могут работать абсолютно независимо и параллельно. К сожалению, это не совсем так: основной проблемой является стандартный распределитель памяти, т.е. операторы new/delete.
Прикрепленные файлы: 1 файл
СПО.docx
— 67.57 Кб (Скачать документ)Создадим каркас приложения для просмотра и редактирования рисунков:
- С помощью команды File | New | Projects | MFC AppWizard (exe) начнем создание приложения. Назовем проект BMViewer.
- На первом шаге выберем тип приложения Multiple documents.
- До шестого шага можно принять все установки по умолчанию.
- На шестом шаге генератора приложений изменим базовый класс облика с предложенного по умолчанию CView на CScrollView. Этот поступок не пройдет бесследно, а обеспечит нас впоследствии возможностью прокручивать изображения в окне облика, если они целиком в нем не поместятся. Здесь же можно изменить имена файлов, в которых будут размещены классы нашего приложения. Например, предложенное имя BMViewerView.h можно заменить на более лаконичное BMView.h.
Вот и все, каркас программы готов. Осталось теперь наделить его полезными качествами.
Класс CRaster для работы с растровыми изображениями
Структура файла с растровым изображением в формате Microsoft Windows Bitmap (BMP) была рассмотрена в прошлой главе, поэтому сразу перейдем к делу.
Создадим в программе специальный класс, который будет отвечать за загрузку и осуществлять поддержку операций по обработке растрового изображения. Назовем класс CRaster. Интерфейс класса CRaster приведен ниже, а реализация методов после прирмера.
| // Raster.h :
interface of CRaster class
// (C) Alexey Polyakov 2002-2003 ////////////////////////////// #ifndef _RASTER_INCLUDED #define _RASTER_INCLUDED // макрос для определения количества байт в выровненной по DWORD строке пикселов в DIB // Width - длина строки в пикселах; BPP - бит на пиксел #define BYTESPERLINE(Width, BPP) ((WORD)((((DWORD)(Width) * \ (DWORD)(BPP) + 31) >> 5))
<< 2) class CRaster { LPBITMAPINFO m_pBMI; //указатель на описание изображения PBYTE m_pData; //указатель на начало растровых данных public: CRaster(); ~CRaster(); void Clear(); //очистка памяти // Возвращает: // указатель на заголовок растра LPBITMAPINFO GetBMInfoPtr(){return m_pBMI;} // указатель на таблицу цветов RGBQUAD* GetBMColorTabPtr(); // ширину в пикселах; LONG GetBMWidth(); // высоту в пикселах LONG GetBMHeight(); // указатель на растровые данные BYTE* GetBMDataPtr(){return m_pData;}; // указатель на пиксел BYTE* GetPixPtr(LONG
x, LONG y); // Загружает из файла BOOL LoadBMP(CString FileName); // Выводит DIB на контекст pDC // x, y - позиция левого верхнего угла области назначения // cx, cy - размер области назначения // x0, y0 - позиция левого верхнего угла выводимой части изображения // cx0, cy0 - размер выводимой части изображения // str_mode – режим масштабирования // rop - растровая операция, определяет способ наложения изображения void DrawBitmap(CDC *pDC, LONG x=0, LONG y=0, LONG cx=0, LONG cy=0, LONG x0=0, LONG y0=0, LONG cx0=0, LONG cy0=0, int str_mode=COLORONCOLOR, DWORD rop=SRCCOPY); // Выводит DIB на контекст pDC с позиции (x,y) в масштабе scale void DrawBitmap(CDC *pDC, LONG x, LONG y, double scale,
int str_mode=COLORONCOLOR, DWORD rop=SRCCOPY); // Записывает BMP в файл BOOL SaveBMP(CString FileName); // Создает копию BOOL CreateCopy(CRaster *pOrg); // Создает растр заданного размера, // совместимый с параметрами BITMAPINFO BOOL CreateCompatible(LPBITMAPINFO pBMI, LONG width=0, LONG height=0); // Возвращает гисторамму изображения BOOL GetHistogram(DWORD *pHist, int Range); }; #endif |
Реализация методов класса CRaster. Файл Raster.cpp
| // Raster.cpp
: implementation of the CRaster class
// (C) Alexey Polyakov 2002-2003 ////////////////////////////// #include "stdafx.h" #include "Raster.h" CRaster::CRaster() { m_pData=NULL; m_pBMI=NULL; } CRaster::~CRaster() { Clear(); }; void CRaster::Clear() { if(m_pData!=NULL) delete[] m_pData; m_pData=NULL; if(m_pBMI!=NULL) delete[] m_pBMI; m_pBMI=NULL; }; RGBQUAD* CRaster::GetBMColorTabPtr() { return(LPRGBQUAD)(((BYTE*)(m_ }; LONG CRaster::GetBMWidth() { if(m_pBMI==NULL) return 0; return m_pBMI->bmiHeader.biWidth; }; LONG CRaster::GetBMHeight() { if(m_pBMI==NULL) return 0; return m_pBMI->bmiHeader.biHeight; }; BYTE* CRaster::GetPixPtr(LONG x, LONG y) { if( x<0 || x>= m_pBMI->bmiHeader.biWidth || y<0 || y>= m_pBMI->bmiHeader.biHeight || m_pData == NULL) return NULL; return (m_pData+(BYTESPERLINE(m_pBMI- m_pBMI->bmiHeader.biBitCount)* }; BOOL CRaster::LoadBMP(CString FileName) { //Очистим Clear(); //Открываем файл CFile File; if(!File.Open(FileName,
CFile::modeRead)) return FALSE; ////////////////////////////// //Загружаем изображение //Читаем заголовок файла. Это дает его размер и положение //начала данных BITMAPFILEHEADER FI; File.Read(&FI,
sizeof(BITMAPFILEHEADER)); //Проверяем, Windows Bitmap изображение ? if(FI.bfType!=0x4D42) { File.Close(); return
FALSE;} //Смещаем позицию File.Seek(sizeof( //Считаем, что все от заголовка файла до начала растровых данных //есть BITMAPINFO //Выделяем память под заголовок m_pBMI=(LPBITMAPINFO)new
BYTE[FI.bfOffBits-sizeof( if(m_pBMI==NULL) { File.Close(); return FALSE;} //Читаем BITMAPINFO File.Read(m_pBMI,
FI.bfOffBits-sizeof( //Умеем работать только с несжатыми данными if(m_pBMI->bmiHeader.
{ File.Close(); return FALSE;} //Переход к началу данных File.Seek(FI.bfOffBits, CFile::begin); //Выделяем память под данные //расчет размера if(m_pBMI->bmiHeader. m_pBMI->bmiHeader.biSizeImage=
BYTESPERLINE(m_pBMI->
m_pBMI->bmiHeader.biBitCount)* m_pData= new BYTE[m_pBMI->bmiHeader. if(m_pData==NULL) { File.Close(); return FALSE;} //Читаем данные File.Read(m_pData,
m_pBMI->bmiHeader.biSizeImage) File.Close(); return TRUE; }; void CRaster::DrawBitmap(CDC *pDC, LONG x/*=0*/, LONG y/*=0*/, LONG cx/*=0*/, LONG cy/*=0*/, LONG x0/*=0*/, LONG y0/*=0*/, LONG cx0/*=0*/, LONG cy0/*=0*/, int str_mode/*=COLORONCOLOR*/, DWORD rop /*=SRCCOPY*/) { if(m_pBMI==NULL || m_pData==NULL) return; //размеры не заданы — габариты в пикселах if(cx==0) cx=GetBMWidth(); if(cy==0) cy=GetBMHeight(); if(cx0==0) cx0=GetBMWidth(); if(cy0==0) cy0=GetBMHeight(); HDC hdc=pDC->GetSafeHdc(); if(hdc==NULL) return; // Установка режима масштабирования int oldStretchMode=pDC-> ::StretchDIBits(hdc, // дескриптор контекста устройства x, y, // позиция в области назначения cx, cy, // размеры обл. назначения x0, y0, //позиция в исходной области cx0, cy0, //размеры исх. обл. m_pData, //данные m_pBMI, //заголовок растра DIB_RGB_COLORS, //опции rop); //код растровой операции if(oldStretchMode!=0) pDC->SetStretchBltMode( }; void CRaster::DrawBitmap(CDC *pDC, LONG x, LONG y, double scale, int str_mode/*=COLORONCOLOR*/, DWORD rop /*=SRCCOPY*/) { if(m_pBMI==NULL ||
m_pData==NULL) return; LONG x0=0, y0=0; LONG cx0=GetBMWidth(); LONG cy0=GetBMHeight(); LONG cx=static_cast<LONG>(scale* LONG cy=static_cast<LONG>(scale* DrawBitmap(pDC, x,
y, cx, cy, x0, y0, cx0, cy0, str_mode, rop); }; BOOL CRaster::SaveBMP(CString FileName) { //Открываем файл CFile File; if(!File.Open(FileName,
CFile::modeCreate|CFile::
return FALSE; ////////////////////////////// //Записываем изображение //Вычислим размер заголовка растра вместе с таблицей цветов DWORD SizeOfBMI= (DWORD)m_pBMI->bmiHeader. m_pBMI->bmiHeader.biClrUsed* // Заголовок файла BITMAPFILEHEADER FI; // Идентификатор типа файла BMP: 0x42 = "B" 0x4d = "M" FI.bfType = 0x4d42; // Размер всего файла вместе с заголовками и данными FI.bfSize = (DWORD) sizeof(BITMAPFILEHEADER) + SizeOfBMI + m_pBMI->bmiHeader.biSizeImage; FI.bfReserved1 = 0; FI.bfReserved2 = 0; // Вычисляем смещение до начала растровых данных FI.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) + SizeOfBMI; // Записываем заголовок файла File.Write(&FI,
sizeof(BITMAPFILEHEADER)); //Записываем BITMAPINFO вместе с таблицей цветов File.Write(m_pBMI,
SizeOfBMI); //Данные File.Write(m_pData,
m_pBMI->bmiHeader.biSizeImage) File.Close(); return TRUE; }; BOOL CRaster::CreateCopy(CRaster *pOrg) { Clear(); if(!pOrg) return FALSE; LPBITMAPINFO pOrgBMI=pOrg->GetBMInfoPtr(); //Вычислим размер заголовка растра вместе с таблицей цветов DWORD SizeOfBMI= (DWORD)pOrgBMI->bmiHeader.
pOrgBMI->bmiHeader.biClrUsed* // Выделим память под заголовок растра m_pBMI=(LPBITMAPINFO)new BYTE[SizeOfBMI]; if(!m_pBMI) return FALSE; // Копируем заголовок растра memcpy(m_pBMI, pOrg->GetBMInfoPtr(),
SizeOfBMI); // Расчет размера памяти под данные if(m_pBMI->bmiHeader. m_pBMI->bmiHeader.biSizeImage= BYTESPERLINE(m_pBMI-> m_pBMI->bmiHeader.biHeight; // Выделяем память под данные m_pData= new BYTE[m_pBMI->bmiHeader. if(!m_pData) return
FALSE; // Копируем данные memcpy(m_pData, pOrg->GetBMDataPtr(),
m_pBMI->bmiHeader.biSizeImage) return TRUE; }; BOOL CRaster::CreateCompatible( { if(!pBMI) return FALSE; if(width==0) width=pBMI->bmiHeader.biWidth; if(height==0)
height=pBMI->bmiHeader. // Проверяем, может существующий растр и так совместим if( m_pBMI!=NULL && // существует
m_pBMI->bmiHeader.biWidth==
m_pBMI->bmiHeader.biHeight==
m_pBMI->bmiHeader.biBitCount== return TRUE; // Растр и так совместим ////////////////////////////// // Создаем совместимый растр Clear(); //Вычислим размер заголовка растра вместе с таблицей цветов DWORD SizeOfBMI= (DWORD) pBMI->bmiHeader.biSize +
pBMI->bmiHeader.biClrUsed* // Выделим память под заголовок растра m_pBMI=(LPBITMAPINFO)new BYTE[SizeOfBMI]; if(!m_pBMI) return
FALSE; // Копируем заголовок растра memcpy(m_pBMI, pBMI,
SizeOfBMI); // Устанавливаем размер m_pBMI->bmiHeader.biWidth= m_pBMI->bmiHeader.biHeight= // Расчет размера памяти под данные m_pBMI->bmiHeader.biSizeImage=
m_pBMI->bmiHeader.biBitCount)* // Выделяем память под данные m_pData= new BYTE[m_pBMI->bmiHeader. if(!m_pData) return
FALSE; return TRUE; }; BOOL CRaster::GetHistogram(DWORD *pHist, int Range) { // Умеет работать только с данными RGB888 if(m_pBMI->bmiHeader. // Обнулим таблицу for(int i=0; i<Range; i++)
pHist[i]=0; LONG DataStrLength= BYTESPERLINE(m_pBMI-> BYTE *pCurPix=NULL; BYTE Brightness=0; for(int y=0, x=0; y<m_pBMI->bmiHeader.biHeight; y++) for(x=0; x<m_pBMI->bmiHeader.biWidth; x++) { // Адрес пиксела
pCurPix=m_pData+y* // Яркость рассчитывается как 0,3*Red+0,59*Green+0,11*Blue, // но пикселные данные хранятся в файле BMP, в порядке BGR Brightness=(BYTE)(( 0.11*(*pCurPix) + 0.59*(*(pCurPix+1))+
0.3*(*(pCurPix+2)))*Range/256) pHist[Brightness]+=1; } return TRUE; }; |
Назначение большинства методов класса CRaster, мы надеемся, понятно из их названий и комментариев. В реализации также нет каких-то особенностей, которые достойны были бы специального рассмотрения. Этот класс никак не связан со структурой приложения, и вы можете использовать его в любой программе "под Windows". Причем если заменить использование классов CFile и CString в методах LoadBMP() и SaveBMP() на реализацию операций с помощью API-функций, то можно обойтись и без MFC. Однако с MFC все же удобнее.
В методе LoadBMP()не реализована распаковка сжатых изображений, поэтому класс CRaster умеет работать только с данными, так сказать, в их натуральном виде.
Кстати, в случае, когда данные хранятся в несжатом виде, мы могли бы и не выделять динамически память под хранение заголовков и данных изображения. Вместо этого можно использовать механизм, называемый "файлы, проецируемые в память". Об этом механизме кратко упоминается в начале главы 2, для освоения же работы с ним можно рекомендовать изучить [9]. В остальном работа с данными осуществлялась бы точно также. Механизм проецирования файлов особенно удобен при работе с файлами большого размера.
Класс CRaster содержит
два метода DrawBitmap, выполняющих вывод
изображения на контекст устройства.
Аргументы одного из методов позволяют
задать положение и размеры выводимой
области исходного изображения
и определить область назначения.
По умолчанию изображение
Режим масштабирования выбирается CDC-методом SetStretchBltMode():
| int SetStretchBltMode( int nStretchMode ); |
Аргумент функции — iStretchMode — режим масштабирования.
Поддерживаются следующие режимы масштабирования:
- BLACKONWHITE — выполняет булеву операцию AND между цветом существующих и удаленных пикселов (при уменьшении размера изображения). Этот режим используется, если масштабируется рисунок "черным по белому", т. е. алгоритм масштабирования будет стараться сохранить черные пикселы;
- COLORONCOLOR — этот режим удаляет (добавляет) строки (столбцы) пикселов без каких-либо попыток сохранить содержащуюся в них информацию. Наиболее быстрый режим. Используется, когда необходимо сохранить цвета изображения неизменными;
- WHITEONBLACK — выполняет булеву операцию OR. Этот режим используется, если масштабируется рисунок "белым по черному";
- HALFTONE — преобразует изображение к заданному размеру и при этом трансформирует цвета так, чтобы средний цвет полученной картинки приближался к исходному цвету. Наиболее медленный режим. Однако масштабированная картинка выглядит лучше за счет сглаживания "лестничного эффекта". Этот режим не работает в Windows 95/98, и похоже, заменяется режимом COLORONCOLOR.
При масштабировании фотографий и цветных рисунков в большинстве случаев наиболее подходящим является режимы COLORONCOLOR и HALFTONE. Далее мы расссмотрим на практике различия между этими режимами.
Метод GetHistogram() предназначен
для получения гистограммы
В проект приложения добавим с помощью команды Project | Add to project | Files файлы Raster.h и Raster.cpp.