Russian Qt Forum

Qt => Пользовательский интерфейс (GUI) => Тема начата: AD от Март 04, 2009, 17:02



Название: альтернатива BITMAPINFO в QImage
Отправлено: AD от Март 04, 2009, 17:02
Есть ли возможность заменить такой код:
Код
C++ (Qt)
BITMAPINFO*         pBmi;
 
 
/// Заполнение картинки (для Windows)
void SubSahara::FillBitmapInfo()
{
#ifdef Q_WS_WIN
memset(pBmi,0,sizeof(BITMAPINFO) + sizeof(RGBQUAD) * 256);
pBmi -> bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pBmi -> bmiHeader.biWidth = spSize.x;
pBmi -> bmiHeader.biHeight = spSize.y;
pBmi -> bmiHeader.biPlanes = 1;
pBmi -> bmiHeader.biBitCount = 8;
pBmi -> bmiHeader.biCompression = BI_RGB;
pBmi -> bmiHeader.biSizeImage = 0;
pBmi -> bmiHeader.biClrUsed = pDisplay -> PaletteSize();
pBmi -> bmiHeader.biClrImportant = 0;
 
for(int i = 0; i < pBmi->bmiHeader.biClrUsed; ++i)
{
pBmi -> bmiColors[i].rgbRed = pDisplay -> GetPaletteR(i);
pBmi -> bmiColors[i].rgbGreen = pDisplay -> GetPaletteG(i);
pBmi -> bmiColors[i].rgbBlue = pDisplay -> GetPaletteB(i);
}
#endif // Q_WS_WIN
}
 
Если заместо BITMAPINFO* использовать QImage*? Буду благодарен за приведенный код!


Название: Re: альтернатива BITMAPINFO в QImage
Отправлено: pastor от Март 04, 2009, 17:18
см. описание QImage в ассистанте. Вот подобный пример из него:

Код
C++ (Qt)
QImage image(3, 3, QImage::Format_RGB32);
QRgb value;
 
value = qRgb(189, 149, 39); // 0xffbd9527
image.setPixel(1, 1, value);
 
value = qRgb(122, 163, 39); // 0xff7aa327
image.setPixel(0, 1, value);
image.setPixel(1, 0, value);
 
value = qRgb(237, 187, 51); // 0xffedba31
image.setPixel(2, 1, value);


Название: Re: альтернатива BITMAPINFO в QImage
Отправлено: Dendy от Март 04, 2009, 17:19
Код
C++ (Qt)
const QSize size( spSize.x, spSize.y );
QImage image( size, QImage::Format_ARGB32 );
int i = 0;
for ( int y = 0; y < size.height(); ++y )
{
 uchar * p = image.scanLine( y );
 for ( int x = 0; x < size.width(); ++x )
 {
   p[0] = 255;
   p[1] = pDisplay->GetPaletteR( i );
   p[2] = pDisplay->GetPaletteG( i );
   p[3] = pDisplay->GetPaletteB( i );
   ++i;
   p += 4;
 }
}
 
pBmi = QPixmap::fromImage( image ).toWinHBITMAP();


Название: Re: альтернатива BITMAPINFO в QImage
Отправлено: AD от Март 04, 2009, 18:21
Цитата: Dendy
///
Спасибо большое! Единственное. Я хочу от BitmapInfo совсем отказаться, поэтому у меня QImage - объект внутри нужного класса.
И еще один момент. Палитра имеет размер, равный
Код
C++ (Qt)
pDisplay -> PaletteSize()
 
И таким образом изменять i, равное width * height, нельзя. Как видоизменить код, исходя из этого ограничения?
Указатель на QImage:
Код
C++ (Qt)
QImage* bitmap;
 
bitmap = new QImage(spSize, QImage::Format_ARGB32);
 


Название: Re: альтернатива BITMAPINFO в QImage
Отправлено: pastor от Март 04, 2009, 18:30
Немного непонятно, pDisplay->PaletteSize() возвращает произведение width * height? Помоему Dendy уже раскрыл вопрос


Название: Re: альтернатива BITMAPINFO в QImage
Отправлено: AD от Март 04, 2009, 18:43
Немного непонятно, pDisplay->PaletteSize() возвращает произведение width * height? Помоему Dendy уже раскрыл вопрос
В том то и дело, что pDisplay -> PaletteSize() < width * height!
из-за этого выдает сбой. пытался сделать так:
Код
C++ (Qt)
int i = 0;
for(int y=0; y<bitmap -> height(); ++y)
{
uchar *p = bitmap -> scanLine(y);
for(int x=0; x<bitmap -> width(); ++x)
{
if(i >= pDisplay -> PaletteSize()) i %= pDisplay -> PaletteSize();
p[0] = 255;
p[1] = pDisplay -> GetPaletteR(i);
p[2] = pDisplay -> GetPaletteG(i);
p[3] = pDisplay -> GetPaletteB(i);
++i;
p += 4;
}
}
 

НО тогда выдается такого рода фигня (см. картинку 1).
А должна быть картинка такого рода (см. картинку 2)



Название: Re: альтернатива BITMAPINFO в QImage
Отправлено: BRE от Март 04, 2009, 19:09
Как устроенна картинка с палитрой:
В палитре хранятся цвета в RGB (например палитра из 5 цветов):
0 - QColor( 0, 0, 0 )
1 - QColor( 255, 255, 255 )
2 - QColor( 255, 0, 0 )
3 - QColor( 0, 255, 0 )
4 - QColor( 0, 0, 255 )

Картинка состоит из индексов в самой палитре (например картинка 7x3):
0 0 1 4 2 3 0
0 3 3 3 2 1 0
1 1 1 1 1 1 1

Чтобы перенести ее в QImage тебе нужно брать индекс цвета, доставать из палитры значения RGB и вставлять в соответствующую позицию QImage.

Изменю код Dendy:
Код
C++ (Qt)
const QSize size( spSize.x, spSize.y );
QImage image( size, QImage::Format_ARGB32 );
for ( int y = 0; y < size.height(); ++y )
{
 uchar * p = image.scanLine( y );
 for ( int x = 0; x < size.width(); ++x )
 {
   // Получаем индекс из исходной картинки
   int i = indexFromSource( x, y );
 
   p[0] = 255;
   p[1] = pDisplay->GetPaletteR( i );
   p[2] = pDisplay->GetPaletteG( i );
   p[3] = pDisplay->GetPaletteB( i );
   p += 4;
 }
}


Название: Re: альтернатива BITMAPINFO в QImage
Отправлено: pastor от Март 04, 2009, 19:18
А pDisplay что это за тип?


Название: Re: альтернатива BITMAPINFO в QImage
Отправлено: AD от Март 04, 2009, 19:22
В доступной палитре, которуая используется, если не ошибаюсь, 256 цветов. В данном случае их 189 (не знаю почему). Какой цвет должен лежать под индексами 190, 191?


Название: Re: альтернатива BITMAPINFO в QImage
Отправлено: BRE от Март 04, 2009, 19:24
В доступной палитре, которуая используется, если не ошибаюсь, 256 цветов. В данном случае их 189 (не знаю почему). Какой цвет должен лежать под индексами 190, 191?
Количество цветов в палитре может быть любым, для этого и введен miHeader.biClrUsed.
Ты бы побольше кода показал...


Название: Re: альтернатива BITMAPINFO в QImage
Отправлено: AD от Март 04, 2009, 19:26
А pDisplay что это за тип?
Не особенно важно. Дело в том, что в проекте используются две библиотеки: Qt and Sahara. 2-ая - это библиотека работы с картографической информацией, изготовлена нашей фирмой. Сейчас задача избавиться от кода зависимости от WINAPI-кода, поэтому и переписываю этот кусок. Заодно решается проблема местоположением виджета (так как в новом варианте нужно только знать ширину и длину виджета).


Название: Re: альтернатива BITMAPINFO в QImage
Отправлено: AD от Март 04, 2009, 19:36

Количество цветов в палитре может быть любым, для этого и введен miHeader.biClrUsed.
Ты бы побольше кода показал...
Вы не так поняли - библиотека Sahara имеет только определенное количество цветов - максимум их - 256 (если я не ошибаюсь).

Больше кода - пожалуйста:
Код
C++ (Qt)
// Класс набора необходимых Сахаровских классов
class SubSahara
{
protected:
SPOINT spSize;
SPOINT begSize;
GPOINT geoCenter;
 
TImageDrawer* pImageDrawer;
MEM256Display*    pDisplay;
   MEM256Device*       pDevice;
TImageManager* pImageManager;
#ifdef Q_WS_WIN
BITMAPINFO*         pBmi;
#endif // Q_WS_WIN
bool bInit; ///< флажок инициализировано или нет
QImage* bitmap; ///< картинка карты
 
protected:
void SaharaInit(int dx, int dy);
void SaharaShutdown();
   void FillBitmapInfo();
public:
SubSahara(int iWidth, int iHeight);
~SubSahara();
#ifdef Q_WS_WIN
void Draw(HDC hdc);
#endif // Q_WS_WIN
void Draw(QPainter* painter);
       void SetBitmapSize(int Xbeg, int Ybeg, int Xend, int Yend);
       void SetBitmapSize(int dx, int dy);
};
 
/// Констуктор: все указатели наобъекты инициируются нулевыми указателями
SubSahara::SubSahara(int iWidth, int iHeight): bInit(false), pImageDrawer(0), pDisplay(0),
pImageManager(0),
pDevice(0), bitmap(new QImage(iWidth, iHeight, QImage::Format_ARGB32))
#ifdef Q_WS_WIN
, pBmi(static_cast<BITMAPINFO*> (HeapAlloc(GetProcessHeap(), 0, sizeof(BITMAPINFO) + sizeof(RGBQUAD) * 256)))
#endif // Q_WS_WIN
{
   SaharaInit(iWidth, iHeight);
}
 
/// Инициализация указателей на объекты соответствующими классами
void SubSahara::SaharaInit(int dx, int dy)
{
   spSize = SPOINT(dx, dy);
ResourceLoader *pRL = new FResourceLoader();
pRL -> RegisterBMPResource(&gbPointSymbBitmap, "SYMBPOINT");
 
   pDisplay = new MEM256Display(*pRL);
   pImageManager = new TImageManager(*pDisplay);
 
   pImageDrawer = new TImageDrawer(nIdDCW = pImageManager -> GetFreeId());
   pImageDrawer -> SetBoundRect(SRECT(SPOINT(0, 0), spSize));
   pImageManager -> RegisterImageStack(pImageDrawer);
 
pDevice = new MEM256Device(*pDisplay, spSize);
#ifdef Q_WS_WIN
FillBitmapInfo();
#endif // Q_WS_WIN
 
   bInit = true;
}
 
/// Функция отрисовки карты (для Windows)
#ifdef Q_WS_WIN
void SubSahara::Draw(HDC hdc)
{
   if(!bInit) return;
 
for(int y = begSize.y; y<spSize.y; ++y)
   {
SetDIBitsToDevice(hdc, ///< handle to DC
begSize.x,                              ///< x-coord of destination upper-left corner
y, ///< y-coord of destination upper-left corner
spSize.x, ///< source rectangle width
1, ///< source rectangle height
0, ///< x-coord of source lower-left corner
0, ///< y-coord of source lower-left corner
0, ///< first scan line in array
1, ///< number of scan lines
///< array of DIB bits
(CONST VOID *)(pDevice->GetSurface() + spSize.x * y),
pBmi, ///< bitmap information
DIB_RGB_COLORS);
   }
}
#endif // Q_WS_WIN
 
/// Функция отрисовки карты
void SubSahara::Draw(QPainter* painter)
{ painter -> drawImage(QPoint(0, 0), *bitmap); }
 
/// Заполнение картинки (для Windows)
void SubSahara::FillBitmapInfo()
{
int i = 0, index = 0;
bool flag = false;
for(int y=0; y<bitmap -> height(); ++y)
{
uchar *p = bitmap -> scanLine(y);
for(int x=0; x<bitmap -> width(); ++x)
{
if(i >= pDisplay -> PaletteSize())
{
flag = true;
i = index % pDisplay -> PaletteSize();
}
if(flag) i = index % pDisplay -> PaletteSize();
p[0] = 255;
p[1] = pDisplay -> GetPaletteR(i);
p[2] = pDisplay -> GetPaletteG(i);
p[3] = pDisplay -> GetPaletteB(i);
++i; ++index;
p += 4;
}
}
}
 
 
/*
/// Заполнение картинки (для Windows)
void SubSahara::FillBitmapInfo()
{
#ifdef Q_WS_WIN
memset(pBmi,0,sizeof(BITMAPINFO) + sizeof(RGBQUAD) * 256);
pBmi -> bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
pBmi -> bmiHeader.biWidth = spSize.x;
pBmi -> bmiHeader.biHeight = spSize.y;
pBmi -> bmiHeader.biPlanes = 1;
pBmi -> bmiHeader.biBitCount = 8;
pBmi -> bmiHeader.biCompression = BI_RGB;
pBmi -> bmiHeader.biSizeImage = 0;
pBmi -> bmiHeader.biClrUsed = pDisplay -> PaletteSize();
pBmi -> bmiHeader.biClrImportant = 0;
 
for(int i = 0; i < pBmi->bmiHeader.biClrUsed; ++i)
{
pBmi -> bmiColors[i].rgbRed = pDisplay -> GetPaletteR(i);
pBmi -> bmiColors[i].rgbGreen = pDisplay -> GetPaletteG(i);
pBmi -> bmiColors[i].rgbBlue = pDisplay -> GetPaletteB(i);
}
#endif // Q_WS_WIN
}*/

 
/// Установка размера картинки
void SubSahara::SetBitmapSize(int dx, int dy)
{
   if(spSize.x != dx || spSize.y != dy)
   {
       spSize = SPOINT(dx, dy);
       pImageDrawer -> SetBoundRect(SRECT(SPOINT(0, 0), spSize));
       pDevice -> SetSize(spSize);
if(bitmap != 0) { delete bitmap; bitmap = 0; }
bitmap = new QImage(dx, dy, QImage::Format_ARGB32);
FillBitmapInfo();
   }
}
 
/// Установка размера картинки
void SubSahara::SetBitmapSize(int Xbeg, int Ybeg, int Xend, int Yend)
{
if(spSize.x != Xend || spSize.y != Yend)
   {
       spSize = SPOINT(Xend, Yend);
begSize = SPOINT(Xbeg, Ybeg);
       pImageDrawer -> SetBoundRect(SRECT(SPOINT(0, 0), spSize));
       pDevice -> SetSize(spSize);
if(bitmap != 0) { delete bitmap; bitmap = 0; }
bitmap = new QImage(Xend, Yend, QImage::Format_ARGB32);
FillBitmapInfo();
   }
}
 
/// Рисование карты и траектории Сахарой
void TrackWidget::paintEvent(QPaintEvent* events)
{
QPainter painter(this);
 
pSahara -> Refresh();
pSahara -> Draw(&painter);
/*#ifdef Q_WS_WIN
pSahara -> Draw(painter.paintEngine() -> getDC()); ///< oтрисовка карты
#endif // Q_WS_WIN*/

if(measure_flag)
{
painter.setPen(QPen(Qt::black, 1, Qt::SolidLine)); ///< установка цвета линий
QPoint p1(calc_distance -> beginPoint()), p2(calc_distance -> endPoint());
painter.drawLine(p1, p2);
}
painter.setPen(QPen(Qt::blue, 1, Qt::SolidLine)); ///< установка цвета траектории
 
QWidget::paintEvent(events);
}
 


Название: Re: альтернатива BITMAPINFO в QImage
Отправлено: BRE от Март 04, 2009, 19:55
Вы не так поняли - библиотека Sahara имеет только определенное количество цветов - максимум их - 256 (если я не ошибаюсь).
Максиму 256, минимум 1.  ;)

У класса MEM256Device есть метод GetSurface(), который возвращает указатель на массив точек (индексов).
Сразу можно сделать функцию (псевдокод):
Код
C++ (Qt)
const QSize size( spSize.x, spSize.y );
QImage image( size, QImage::Format_ARGB32 );
for ( int y = 0; y < size.height(); ++y )
{
 const uchar *scrImage = pDevice->GetSurface() + y * size.width();
 uchar * p = image.scanLine( y );
 for ( int x = 0; x < size.width(); ++x )
 {
   // Получаем индекс из исходной картинки
   uchar i = *srcImage++;
 
   p[0] = 255;
   p[1] = pDisplay->GetPaletteR( i );
   p[2] = pDisplay->GetPaletteG( i );
   p[3] = pDisplay->GetPaletteB( i );
   p += 4;
 }
}
 


Название: Re: альтернатива BITMAPINFO в QImage
Отправлено: AD от Март 04, 2009, 21:07
Максиму 256, минимум 1.  ;)

У класса MEM256Device есть метод GetSurface(), который возвращает указатель на массив точек (индексов).
Сразу можно сделать функцию (псевдокод):
Спасибо. Почти работает! Но картинки получаются что-ли инвертированными. Вот так: (1ый - p[0] = 255) (2ой - p[0] = 0).
Придется вручную подбирать цвет или можно решить проблему?



Название: Re: альтернатива BITMAPINFO в QImage
Отправлено: BRE от Март 04, 2009, 21:11
Спасибо. Почти работает! Но картинки получаются что-ли инвертированными. Вот так: (1ый - p[0] = 255) (2ой - p[0] = 0).
Придется вручную подбирать цвет или можно решить проблему?
Попробуй здесь порядок компонент поменять:
Код
C++ (Qt)
p[3] = 255;
   p[2] = pDisplay->GetPaletteR( i );
   p[1] = pDisplay->GetPaletteG( i );
   p[0] = pDisplay->GetPaletteB( i );
 


Название: Re: альтернатива BITMAPINFO в QImage
Отправлено: AD от Март 04, 2009, 21:31
Еще раз спасибо. Уже сам додумался.
3-й компонент - 255.
А раскраска - BGR!
Thanks! :))))


Название: Re: альтернатива BITMAPINFO в QImage
Отправлено: BRE от Март 04, 2009, 21:34
Еще раз спасибо. Уже сам додумался.
3-й компонент - 255.
А раскраска - BGR!
Thanks! :))))
Точнее ARGB
DWORD 0xAARRGGBB в памяти сохраняется как BB GG RR AA (увеличение адресов --->).


Название: Re: альтернатива BITMAPINFO в QImage
Отправлено: BRE от Март 06, 2009, 11:54
AD, просмотрел я, что QImage и так поддерживает картинки с палитрой.
Т.е. можно картинку не переводить в ARGB самому, это может делать сама Qt при отрисовке.

Устанавливаем формат QImage на QImage::Format_Indexed8 и настраиваем палитру:
void QImage::setColorTable ( const QVector<QRgb> colors ).
Палитру настраиваем из pDisplay->GetPaletteX(...).
Данные берем из pDevice->GetSurface().
Подробности в Assistant -> QImage -> Pixel Manipulation.  :)