Многопоточное программирование на C++

Реферат, 24 Января 2012, автор: пользователь скрыл имя

Краткое описание


Для начала поговорим о подводных камнях C++ применительно к MT программированию. На первый взгляд может показаться, что написанные на C++ потоки, не изменяющие одни и те же данные одновременно, могут работать абсолютно независимо и параллельно. К сожалению, это не совсем так: основной проблемой является стандартный распределитель памяти, т.е. операторы new/delete.

Прикрепленные файлы: 1 файл

СПО.docx

— 67.57 Кб (Скачать документ)

Создадим каркас приложения для просмотра и редактирования рисунков:

  1. С помощью команды File | New | Projects | MFC AppWizard (exe) начнем создание приложения. Назовем проект BMViewer.
  2. На первом шаге выберем тип приложения Multiple documents.
  3. До шестого шага можно принять все установки по умолчанию.
  4. На шестом шаге генератора приложений изменим базовый класс облика с предложенного по умолчанию 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_pBMI))+sizeof(BITMAPINFOHEADER));

}; 

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->bmiHeader.biWidth,

   m_pBMI->bmiHeader.biBitCount)*y + x*m_pBMI->bmiHeader.biBitCount/8));

}; 

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(BITMAPFILEHEADER), CFile::begin); 

   //Считаем, что все от заголовка файла до начала растровых данных

   //есть BITMAPINFO

   //Выделяем память под заголовок

   m_pBMI=(LPBITMAPINFO)new BYTE[FI.bfOffBits-sizeof(BITMAPFILEHEADER)];

   if(m_pBMI==NULL) { File.Close(); return FALSE;}

   //Читаем BITMAPINFO

   File.Read(m_pBMI, FI.bfOffBits-sizeof(BITMAPFILEHEADER)); 

   //Умеем работать только с несжатыми данными

   if(m_pBMI->bmiHeader.biCompression!=0)

      { File.Close(); return FALSE;} 

   //Переход к началу данных

   File.Seek(FI.bfOffBits, CFile::begin);

   //Выделяем память под данные

   //расчет размера

   if(m_pBMI->bmiHeader.biSizeImage==0)

      m_pBMI->bmiHeader.biSizeImage=

               BYTESPERLINE(m_pBMI->bmiHeader.biWidth,

               m_pBMI->bmiHeader.biBitCount)*m_pBMI->bmiHeader.biHeight; 

   m_pData= new BYTE[m_pBMI->bmiHeader.biSizeImage];

   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->SetStretchBltMode(str_mode);

   ::StretchDIBits(hdc,      // дескриптор контекста устройства

                   x, y,     // позиция в области назначения

                   cx, cy,   // размеры обл. назначения

                   x0, y0,   //позиция в исходной области

                   cx0, cy0, //размеры исх. обл.

                   m_pData,  //данные

                   m_pBMI,   //заголовок растра

                   DIB_RGB_COLORS,   //опции

                   rop);     //код растровой операции

if(oldStretchMode!=0)

   pDC->SetStretchBltMode(oldStretchMode);

}; 

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*cx0+0.5);

   LONG cy=static_cast<LONG>(scale*cy0+0.5); 

   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::modeWrite))

      return FALSE; 

   ////////////////////////////////////////////////////////

   //Записываем изображение

   //Вычислим размер заголовка растра вместе с таблицей цветов

   DWORD SizeOfBMI= (DWORD)m_pBMI->bmiHeader.biSize +

   m_pBMI->bmiHeader.biClrUsed*sizeof(RGBQUAD); 

   // Заголовок файла

   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.biSize +

                    pOrgBMI->bmiHeader.biClrUsed*sizeof(RGBQUAD); 

   // Выделим память под заголовок растра

   m_pBMI=(LPBITMAPINFO)new BYTE[SizeOfBMI];

   if(!m_pBMI) return FALSE;

   // Копируем заголовок растра

   memcpy(m_pBMI, pOrg->GetBMInfoPtr(), SizeOfBMI); 

   // Расчет размера памяти под данные

   if(m_pBMI->bmiHeader.biSizeImage==0)

   m_pBMI->bmiHeader.biSizeImage=

   BYTESPERLINE(m_pBMI->bmiHeader.biWidth, m_pBMI->bmiHeader.biBitCount)*

   m_pBMI->bmiHeader.biHeight;

   // Выделяем память под данные

   m_pData= new BYTE[m_pBMI->bmiHeader.biSizeImage];

   if(!m_pData) return FALSE; 

   // Копируем данные

   memcpy(m_pData, pOrg->GetBMDataPtr(), m_pBMI->bmiHeader.biSizeImage); 

   return TRUE;

}; 

BOOL CRaster::CreateCompatible(LPBITMAPINFO pBMI, LONG width/*=0*/,

                                                  LONG height/*=0*/)

{

   if(!pBMI) return FALSE; 

   if(width==0)    width=pBMI->bmiHeader.biWidth;

   if(height==0)   height=pBMI->bmiHeader.biHeight; 

   // Проверяем, может существующий растр и так совместим

   if( m_pBMI!=NULL &&                       // существует

       m_pBMI->bmiHeader.biWidth==width &&   // такого же размера

       m_pBMI->bmiHeader.biHeight==height && // и глубина цвета совпадает

       m_pBMI->bmiHeader.biBitCount==pBMI->bmiHeader.biBitCount)

      return TRUE; // Растр и так совместим

   ////////////////////////////////////////////////////////

   // Создаем совместимый растр

   Clear();

   //Вычислим размер заголовка растра вместе с таблицей цветов

   DWORD SizeOfBMI= (DWORD) pBMI->bmiHeader.biSize +

                 pBMI->bmiHeader.biClrUsed*sizeof(RGBQUAD); 

   // Выделим память под заголовок растра

   m_pBMI=(LPBITMAPINFO)new BYTE[SizeOfBMI];

   if(!m_pBMI) return FALSE; 

   // Копируем заголовок растра

   memcpy(m_pBMI, pBMI, SizeOfBMI); 

   // Устанавливаем размер

   m_pBMI->bmiHeader.biWidth=width;

   m_pBMI->bmiHeader.biHeight=height; 

   // Расчет размера памяти под данные

   m_pBMI->bmiHeader.biSizeImage=BYTESPERLINE(m_pBMI->bmiHeader.biWidth,

              m_pBMI->bmiHeader.biBitCount)*m_pBMI->bmiHeader.biHeight;

   // Выделяем память под данные

   m_pData= new BYTE[m_pBMI->bmiHeader.biSizeImage];

   if(!m_pData) return FALSE; 

   return TRUE;

}; 

BOOL CRaster::GetHistogram(DWORD *pHist, int Range)

{

   // Умеет работать только с данными RGB888

   if(m_pBMI->bmiHeader.biBitCount!=24) return FALSE; 

   // Обнулим таблицу

   for(int i=0; i<Range; i++)

      pHist[i]=0; 

   LONG DataStrLength=

   BYTESPERLINE(m_pBMI->bmiHeader.biWidth, m_pBMI->bmiHeader.biBitCount);

   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*DataStrLength+x*3; 

         // Яркость рассчитывается как 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, выполняющих вывод  изображения на контекст устройства. Аргументы одного из методов позволяют  задать положение и размеры выводимой  области исходного изображения  и определить область назначения. По умолчанию изображение выводится  полностью в масштабе 1:1, однако с  помощью аргументов этой функции  можно и изменить масштаб. Второй метод позволяет просто указать  позицию начала вывода и масштаб, в котором должно быть нарисовано изображение. Оба метода внутри используют мощную API-функцию StretchDIBits(). Начиная с Windows 98, реализация этой функции умеет выводить растровые данные в форматах JPEG и PNG. Класс CDC, который мы обычно используем для рисования, имеет похожий метод CDC::StretchBlt(), но он в качестве исходного изображения просит указать контекст устройства, а не указатель на данные.

Режим масштабирования  выбирается 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.

Информация о работе Многопоточное программирование на C++