Перейти к содержимому
Compvision.ru
DirecTwiX

GetDIBits пишет за пределы массивы (Heap corruption detected)

Recommended Posts

Неделю назад написал такую функцию:

void Bmp2Mat(HDC hDC, HBITMAP hBmp, cv::Mat &img8UC3)

{

	int size=3*img8UC3.cols*img8UC3.rows;

	BYTE *bmpData = new BYTE[size];


	BITMAPINFOHEADER bmi={0};


    bmi.biSize = sizeof(BITMAPINFOHEADER);

    bmi.biPlanes = 1;

    bmi.biBitCount = 24;

	bmi.biWidth = img8UC3.cols;

	bmi.biHeight = -img8UC3.rows;

    bmi.biCompression = BI_RGB;


	GetDIBits(hDC, hBmp, 0, img8UC3.rows, bmpData, (BITMAPINFO*)&bmi, DIB_RGB_COLORS);


	memcpy(img8UC3.data, bmpData, size);

	delete [] bmpData;

}

Вытаскивал матрицу из HDC... Работала (да и до сих пор работает) без пререканий, но только на одном окне (мне в принципе другие не нужны, но разобраться хочется). Сегодня решил потестить для окна калькулятора...

И что я увидел? Ошибку "Heap corruption detected"... Оказалось, что GetDIBits каким-то образом пишет за пределы массива... Гуглил-гуглил и вот что нашёл: http://www.rsdn.ru/forum/winapi/111983.flat.aspx

Там похожая проблема, но только для 4 бит (сказали, что функа ещё палитру пытается всунуть). Мне добавить в bmi массив размера 2^24????

На msdn вроде написано, что палитру не должно записывать =\

Да и почему для одного окна работает, а для другого сыпется?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

1. А почему высоту ты со знаком "-" записываешь?

2. В твоём случае значения, скорее всего, не имеет, но размер изображения не всегда равен 3*img8UC3.cols*img8UC3.rows. Если cols (вдруг!) будет нечётным, то можешь получить затирание памяти. Обычно строки в памяти выравниваются по 4 байта.

3. Почему бы не добавить палитру, как сказал Alex Fedotov? Там же совсем не 2^24 байт.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

1. Иначе изображение будет перевёрнутым

2. Какие строки имеются в виду? GetDIBits и Img.data - это один длинный участок в памяти

3. А сколько? 24 бита на цвет -> всего 2^24 возможный цветов

Проблема решена. Оказалось, что везде работает, кроме калькулятора (win7). У него с шириной какая-то бага. По факту она 1 пиксель больше чем выдаёт GetWindowRect/GetClientRect =)

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Выравнивание это. Каждая строка для 32 битного режима должна быть выравнена на границе 4 байт. А так как пиксель 3 байта, то при нечётной длине строки имеем неправильный размер.

  • Like 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

2. Какие строки имеются в виду? GetDIBits и Img.data - это один длинный участок в памяти

Любые. Pavia00 прав. Длина строки выровнена на 4 байта, это надо учитывать. Именно для этого в структуре IplImage есть значение widthStep.

Можешь, кстати, провести эксперимент. Создай в Паинте два 24 битных bmp одинаковой высоты 12 пикселей, но разной ширины: один 11 пикселей, а другой 12. И сравни размеры получившихся файлов - они будут одинаковыми. Так же ведут себя поверхности и текстуры, хотя они иногда вообще до ближайшей сверху степени двойки выравниваются.

  • Like 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Ладно... Уговорили) У калькулятора ширина и в правду нечётной была =\

Сколько тогда памяти выделять? Вот немного переписал:

void Bmp2Mat(HDC hDC, HBITMAP hBmp, cv::Mat &img)

{

	BITMAPINFOHEADER bmi={0};


	bmi.biSize = sizeof(BITMAPINFOHEADER);

	GetDIBits(hDC, hBmp, 0, 0, 0, (BITMAPINFO*)&bmi, DIB_RGB_COLORS);

	bmi.biPlanes=1;

	bmi.biBitCount = 24;

	bmi.biCompression = BI_RGB;


	int size=3*bmi.biHeight*bmi.biWidth;

	bmi.biHeight=-bmi.biHeight;

	BYTE *bmpData = new BYTE[size];

	GetDIBits(hDC, hBmp, 0, -bmi.biHeight, bmpData, (BITMAPINFO*)&bmi, DIB_RGB_COLORS);


	img=cv::Mat(-bmi.biHeight, bmi.biWidth, CV_8UC3);

	memcpy(img.data, bmpData, size);

	delete [] bmpData;

}
UPD: если перед int size=... добавить
	if (bmi.biWidth%4)

		bmi.biWidth=4-bmi.biWidth%4+bmi.biWidth;

То всё будет работать... Но если ширина изображения не кратна 4, то справа появляются лишние столбы (от 1 до 3). Пробовал уменьшать размер матрицы - безрезультатно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Всё правильно делаешь. Я не работал с cv::Mat, а всё больше с IplImage. Там всё решается с помощью widthStep

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Создайте учётную запись или войдите для комментирования

Вы должны быть пользователем, чтобы оставить комментарий

Создать учётную запись

Зарегистрируйтесь для создания учётной записи. Это просто!

Зарегистрировать учётную запись

Войти

Уже зарегистрированы? Войдите здесь.

Войти сейчас


  • Сейчас на странице   0 пользователей

    Нет пользователей, просматривающих эту страницу

×