LINUX.ORG.RU

[openGL] Отображение jpeg'ов

 


0

0

Как без оперирования с текстурами (т.к. при этом увеличивается время отрисовки графики) можно без извращений добиться нормального отображения jpeg-изображения в окне openGL?

Дело в том, что в дурной системе координат openGL начало координат находится в левом нижнем углу. Для отображения bmp это идеально (т.к. в bmp изображение хранится «вверх ногами»), а вот в jpeg'ах изображение хранится по-нормальному: начиная с верхней строки.

Пока пользуюсь костылем, чреватым случайными segFault'ами, получая изображение такой функцией:

GLubyte *uncompressImage(JOCTET *inbuf){
	static struct jpeg_decompress_struct cinfo;
	struct jpeg_error_mgr jerr;
	static GLubyte *imPtr = NULL;
	static int bufferSize;
	int readlines, linesize;
	GLubyte *ptr;
	cinfo.err = jpeg_std_error(&jerr);
	jpeg_create_decompress(&cinfo);
	jpeg_mem_src(&cinfo, inbuf, bufsize); 
	jpeg_read_header(&cinfo, TRUE);
	jpeg_start_decompress(&cinfo);
	if(!imPtr){
		bufferSize = GRAB_WIDTH * GRAB_HEIGHT * cinfo.num_components;
		imPtr = (GLubyte *)malloc(bufferSize);
	}
	linesize = cinfo.num_components * cinfo.output_width;
	ptr = imPtr + (bufferSize - linesize);
	while(cinfo.output_scanline < cinfo.output_height){
		readlines = jpeg_read_scanlines(&cinfo, &ptr, 1);
		ptr -= readlines * linesize;
	}
	jpeg_finish_decompress(&cinfo); 
	return imPtr;
}
Не факт, что операция ptr -= readlines * linesize; не приведет однажды к выходу за нижнюю границу выделенной памяти.

☆☆☆☆☆

Последнее исправление: Eddy_Em (всего исправлений: 1)

Не надо ничего переворачивать руками, просто вертексы в другом порядке поставь, OpenGL сам всё сделает.

Покажи код отрисовки.

Reset ★★★★★
()
Ответ на: комментарий от Reset

Покажи код отрисовки.

В функции display:

		glRasterPos2f(xoffset, yoffset);
		glPixelZoom(xscale, yscale);
		glDrawPixels(width, height, GL_RGB, GL_UNSIGNED_BYTE, ptr);
где ptr - указатель на область памяти с картинкой.

Функция reshape:

void Resize(int width, int height){
	int window = glutGetWindow();
	if(window == mainWindow){
		MainWidth  = width;
		MainHeight = height;
	}
	else{
		WavWidth = width;
		WavHeight = height;
	}
	glViewport(0, 0, width, height);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	GLfloat W = (GLfloat)width; GLfloat H = (GLfloat)height;
	glOrtho(0.0, W, 0.0, H, -100.0, 100.0);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
}

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

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

glGenTextures(1, &id);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

glTexImage2D(GL_TEXTURE_2D, 0, 4, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);

Заливка в видеопамять

// p - указатель на массив с изображением
glBindTexture(GL_TEXTURE_2D, id);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, p);
// тут p больше не нужен

Отрисовка

glBindTexture(GL_TEXTURE_2D, id);
glBegin(GL_QUADS);
glTexCoord2f(0, 0); glVertex3f(x1, y1, z);
glTexCoord2f(1, 0); glVertex3f(x2, y1, z);
glTexCoord2f(1, 1); glVertex3f(x2, y2, z);
glTexCoord2f(0, 1); glVertex3f(x1, y2, z);
glEnd();

меняя вертексы, можно вертеть текстуру как угодно

Reset ★★★★★
()

> Как без оперирования с текстурами (т.к. при этом увеличивается время отрисовки графики)

???

А вообще всё правильно тебе посоветовали: Ъ OpenGL way - заливать в текстуру и рисовать quad.

runtime ★★★★
()
Ответ на: комментарий от Eddy_Em

После glGenTextures(1, &id); я забыл поставить glBindTexture(GL_TEXTURE_2D, id); Ну и формат исправь.

И попробуй для начала вывести нетектурированный квадрат:

glColor3f(1.0, 0.0, 0.0)
glBegin(GL_QUADS); 
glVertex3f(x1, y1, z); 
glVertex3f(x2, y1, z); 
glVertex3f(x2, y2, z); 
glVertex3f(x1, y2, z); 
glEnd(); 

Reset ★★★★★
()
Ответ на: комментарий от Reset

После glGenTextures(1, &id); я забыл поставить glBindTexture(GL_TEXTURE_2D, id);

Да я-то поставил. Получается следующее. Сначала вызываю glEnable(GL_TEXTURE_2D); Далее, инициализация окна:

void createWindow(int *window){
	char *Name;
	GLuint *Tex;
	if(*window != -1) return;
	if(window == &mainWindow){
		Name = strdup("Main window");
		Tex = &mainTex;
	}
	else{
		if(window == &WaveWindow){
			Name = strdup("Wavelet viewer");
			Tex = &WaveTex;
		}
		else{
			Name = strdup("Subwindow");
			Tex = &SubTex;
		}
	}
	*window = glutCreateWindow(Name);
	glutIdleFunc(Redraw);
	glutReshapeFunc(Resize);
	glutDisplayFunc(RedrawWindow);
	glutKeyboardFunc(keyPressed);
	glutSpecialFunc(keySpPressed);
	glutMouseFunc(mousePressed);
	glutMotionFunc(mouseMove);
	createMenu(window);
	totWindows++;
	glGenTextures(1, Tex);
	glBindTexture(GL_TEXTURE_2D, &Tex);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); 
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); 
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexImage2D(GL_TEXTURE_2D, 0, 3, GRAB_WIDTH, GRAB_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); 
	free(Name);
}
В функции RedrawWindow:
...
		glBindTexture(GL_TEXTURE_2D, *Tex); 
		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
		glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, ptr);
			glBegin(GL_QUADS);
	glTexCoord2f(0.0, 0.0); glVertex3f(0.0, 0.0, 0.0);
	glTexCoord2f(0.0, 1.0); glVertex3f(0.0, 200., 0.0);
	glTexCoord2f(1.0, 1.0); glVertex3f(200., 200., 0.0);
	glTexCoord2f(1.0, 0.0); glVertex3f(200., 0.0, 0.0);
	glEnd();
...
Функция Resize:
void Resize(int width, int height){
	int window = glutGetWindow();
	if(window == mainWindow){
		MainWidth  = width;
		MainHeight = height;
	}
	else{
		WavWidth = width;
		WavHeight = height;
	}
	glViewport(0, 0, width, height);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	GLfloat W = (GLfloat)width; GLfloat H = (GLfloat)height;
	glOrtho(0.0, W, 0.0, H, -100.0, 100.0);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(0., 0., 10.0, 0., 0., 0.0, 0.0, 1.0, 0.0);
}

Однако, текстуры нет. Только цветной квадрат. Кстати, а разве не надо вызывать glGenerateMipmapEXT(GL_TEXTURE_2D) для отображения структуры?

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

Mipmap нужен только для красивого и эффективного масштабирования. Без него всё будет работать.

GRAB_WIDTH, GRAB_HEIGHT это что такое ? Оно должно быть >= размеру картики. Если оно больше размера картинки, то координаты текстуры у тебя будут не от 0 до 1, а от 0 до width/GRAB_WIDTH и от 0 до height/GRAB_HEIGHT.

Еще с GLuint *Tex; ты чего-то намутил.

Подергай glGetError() после каждого вызова, тогда ясно станет что не работает.

Reset ★★★★★
()
Ответ на: комментарий от Reset

GRAB_WIDTH, GRAB_HEIGHT это что такое ?

Ширина и высота захваченного с камеры изображения.

Еще с GLuint *Tex; ты чего-то намутил.

У меня одновременно открыто по крайней мере два окна (а может быть и три). Везде разное изображение, и везде оно динамическое.

Подергай glGetError() после каждого вызова, тогда ясно станет что не работает.

Спасибо, попробую.

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

У меня одновременно открыто по крайней мере два окна (а может быть и три). Везде разное изображение, и везде оно динамическое.

Вот тут ты пишешь по левому адресу

   GLuint *Tex; 
   glGenTextures(1, Tex); 

А вот тут делаешь непойми чего:

   glBindTexture(GL_TEXTURE_2D, &Tex); 

Должно быть так

   GLuint Tex; 
   glGenTextures(1, &Tex); 
   glBindTexture(GL_TEXTURE_2D, Tex); 

Reset ★★★★★
()
Ответ на: комментарий от Eddy_Em

Нет, для окон разные идентификаторы должны быть. Но ты писать должен в свою память. Поэтому должно быть именно GLuint Tex или GLuint Tex[3], если сразу 3 текстуры создать хочешь ...

Сделай для начала чтоб работало в одном окне. Кстати что такое окно в терминах GLUT ? Там новый контекст создается ? Если так, то перед gl вызовами надо делать makeCurrent для контекста, который относится к нужному окну.

Reset ★★★★★
()
Ответ на: комментарий от Reset

В одном из примеров, что я нашел, отрисовка работала и без glGenTextures(1, Tex) - эта функция просто возращает номер свободной текстуры (можно и без нее обойтись).

В каждом окне у меня отображаются разные изображения. Изменение контекста производится при помощи glutSetWindow(...).

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

Ну а чего ты туда передаешь? То самое, что создал с помощью GenTextures или херню? Посмотри в gdb. Также убедись, что glBindTexture НЕ вызывается между glBegin/glEnd

Reset ★★★★★
()
Ответ на: комментарий от Reset

Ошибка была из-за того, что я в одном окне использовал текстуру для другого. Изменил немного - во всех контрольных точках получаю отсутствие ошибок. Однако, не рисует, зараза.

Ладно, поковыряюсь еще в примерах, может, найду, в чем ошибка.

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Reset

Заработало! Но с glTexImage2D, glTexSubImage2D так и не завелся. Да, инициализация была такой:

	glEnable(GL_TEXTURE_2D);
	glGenTextures(1, &Tex[n]);
	glBindTexture(GL_TEXTURE_2D, Tex[n]);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
а отрисовка вот такой:
		glBindTexture(GL_TEXTURE_2D, Tex[n]);
		glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
		glTexImage2D(GL_TEXTURE_2D, 0, 3, GRAB_WIDTH, GRAB_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, ptr); 
		glBegin(GL_QUADS); 
		glTexCoord2f(0., 1.); glVertex3f(0., 0., 0.); 
		glTexCoord2f(0., 0.); glVertex3f(0., height, 0.); 
		glTexCoord2f(1., 0.); glVertex3f(width, height, 0.); 
		glTexCoord2f(1., 1.); glVertex3f(width, 0., 0.); 
		glEnd();
Правда, изображение появилось лишь в основном окне - во вспомогательном ничего нет. Но это уже мелочи.

Спасибо за советы.

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

Смысла разбиения одного glTexImage2D на два glTexImage2D/glTexSubImage2D, в том, что glTexImage2D одновременно аллоцирует память и копирует данные, а glTexSubImage2D только копирует данные. Тебе же не надо постоянно переаллоцировать текстуру, поэтому, если делать два вызова, то отрисовка будет работать быстрее.

Reset ★★★★★
()
Ответ на: комментарий от Reset

Заработало и с glTexImage2D, видимо, когда я пробовал первый раз, не показывало из-за неправильных параметров текстуры в glTexParameterf. Сейчас разбираюсь, почему не отображается вторая картинка.

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

> Что именно вас интересует?

Мне также нужно отображать jpeg-и (вернее, декодированный mjpeg, а также битмапы после машинной обработки кадров), и OpenGL - один из рассматриваемых бэк-ендов. Поэтому хочется иметь полностью работающий код, который можно было бы просто скопипастить.

Manhunt ★★★★★
()
Ответ на: комментарий от Manhunt

PS Если я когда-либо опубликую упомянутую поделку, она будет под gpl/lgpl.

Manhunt ★★★★★
()
Ответ на: комментарий от Manhunt

Если не забуду - выложу. На сегодня хватит: пока нет ливня и выглянуло Солнце, пойду покатаюсь не велосипеде...

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

Кстати в jpeg картинка хранится в YUV, поэтому по хорошему её надо распаковывать как YUV без преобразования в RGB на процессоре, а потом шейдером на самой видюшке делать преобразование. Тогда вообще летать всё будет.

Reset ★★★★★
()
Ответ на: комментарий от Reset

Кстати в jpeg картинка хранится в YUV

Ой ли? Я никаких преобразований yuv->rgb не делаю, просто сразу декодированную картинку считаю rgb. И изображение верно отображается.

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

Википедия говорит, что в YUV. ffmpeg при декодировании тоже возвращает YUV. Возможно libjpeg для удобства сама делает преобразование и по хорошему это надо отключать для быстрого вывода.

Reset ★★★★★
()
Ответ на: комментарий от Manhunt

Жуткая альфа-версия с кучей «левого» и неоптимизированного кода, но работающая. Остается оптимизировать вычисления вейвлетов, добавить вывод гистограммы по выбранной совокупности вейвлет-коэффициентов и попробовать реализовать кое-что из советов Reset. Да, еще стоит попробовать унифицировать захват видео по совету mv.

В общем, работы еще полно.

Eddy_Em ☆☆☆☆☆
() автор топика
Ответ на: комментарий от Eddy_Em

Я свой вывод делал на основе вот этого http://www.fourcc.org/source/YUV420P-OpenGL-GLSLang.c

Тут существенно использование OpenGL 2.0 и расширений от nvidia.

Я кучу исправлений внес в это, в том числе сделал поддержку всех видеокарт, поддерживающих расширение Fragment Program, сделал поддержку OpenGL 1.4, избавился от расширений nvidia, сделал поддержку формата argb (из Qt). В итоге от оригинала в моем коде ничего не осталось :)

Reset ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.