c++ - Image scaling (KeepAspectRatioByExpanding) through OpenGL -



c++ - Image scaling (KeepAspectRatioByExpanding) through OpenGL -

i'm trying implement image scaling in opengl using gltexcoord2f() , glvertex2f().

let me explain: after loading qimage , sending gpu glteximage2d() have perform image scaling operations based on qt's specification. qt defines these 3 operations (see image below):

i think way since application qt plugin , task needs done within paint() method of class. ignoreaspectratio operation pretty straight forwards , it's working right now. keepaspectratio gave me problem it's working. unfortunally, keepaspectratiobyexpanding giving me headaches.

i'm sharing i've done far , appreciate help on issue:

main.cpp:

#include "oglwindow.h" #include <qtgui/qapplication> int main(int argc, char *argv[]) { qapplication a(argc, argv); oglwindow w; w.show(); homecoming a.exec(); }

oglwindow.cpp:

#include "oglwindow.h" #include "glwidget.h" #include <qgridlayout> oglwindow::oglwindow(qwidget *parent, qt::wflags flags) : qmainwindow(parent, flags) { ui.setupui(this); glwidget *opengl = new glwidget(this); qgridlayout *layout = new qgridlayout; setlayout(layout); } oglwindow::~oglwindow() { }

oglwindow.h:

#ifndef oglwindow_h #define oglwindow_h #include <qtgui/qmainwindow> #include "ui_yuv_to_rgb.h" class oglwindow : public qmainwindow { q_object public: oglwindow(qwidget *parent = 0, qt::wflags flags = 0); ~oglwindow(); private: ui::oglwindowclass ui; }; #endif // oglwindow_h

glwidget.cpp:

#ifdef _msc_ver #include <windows.h> #include <gl/glew.h> #include <gl/gl.h> #else #include <gl/gl.h> #endif #include "glwidget.h" #include <qdebug> #include <iostream> #include <fstream> #include <assert.h> static const char *p_s_fragment_shader = "#extension gl_arb_texture_rectangle : enable\n" "uniform sampler2drect tex;" "uniform float imgheight, chromaheight_half, chromawidth;" "void main()" "{" " vec2 t = gl_texcoord[0].xy;" // texcoord fixed-function pipeline " float cby = imgheight + floor(t.y / 4.0);" " float cry = imgheight + chromaheight_half + floor(t.y / 4.0);" " float cbcrx = floor(t.x / 2.0) + chromawidth * floor(mod(t.y, 2.0));" " float cb = texture2drect(tex, vec2(cbcrx, cby)).x - .5;" " float cr = texture2drect(tex, vec2(cbcrx, cry)).x - .5;" " float y = texture2drect(tex, t).x;" // redundant texture read optimized away texture cache " float r = y + 1.28033 * cr;" " float g = y - .21482 * cb - .38059 * cr;" " float b = y + 2.12798 * cb;" " gl_fragcolor = vec4(r, g, b, 1.0);" "}"; glwidget::glwidget(qwidget *parent) : qglwidget(qglformat(qgl::samplebuffers), parent), _frame(null) { setautofillbackground(false); setminimumsize(640, 480); /* load 1280x768 yv12 frame disk */ _frame = new qimage(1280, 768, qimage::format_rgb888); if (!_frame) { qdebug() << "> glwidget::glwidget !!! failed create _frame"; return; } std::ifstream yuv_file("bloco.yv12", std::ios::in | std::ios::binary | std::ios::ate); if (!yuv_file.is_open()) { qdebug() << "> glwidget::glwidget !!! failed load yuv file"; return; } int yuv_file_sz = yuv_file.tellg(); unsigned char* memblock = new unsigned char[yuv_file_sz]; if (!memblock) { qdebug() << "> glwidget::glwidget !!! failed allocate memblock"; return; } yuv_file.seekg(0, std::ios::beg); yuv_file.read((char*)memblock, yuv_file_sz); yuv_file.close(); qmemcopy(_frame->scanline(0), memblock, yuv_file_sz); delete[] memblock; } glwidget::~glwidget() { if (_frame) delete _frame; } void glwidget::paintevent(qpaintevent *event) { qpainter painter(this); painter.setrenderhint(qpainter::antialiasing); qdebug() << "> glwidget::paintevent opengl:" << ((painter.paintengine()->type() != qpaintengine::opengl && painter.paintengine()->type() != qpaintengine::opengl2) ? "disabled" : "enabled"); qglcontext* context = const_cast<qglcontext *>(qglcontext::currentcontext()); if (!context) { qdebug() << "> glwidget::paintevent !!! unable retrieve ogl context"; return; } context->makecurrent(); painter.fillrect(qrectf(qpoint(0, 0), qsize(1280, 768)), qt::black); painter.beginnativepainting(); /* initialize gl extensions */ glenum err = glewinit(); if (err != glew_ok) { qdebug() << "> glwidget::paintevent !!! glewinit failed with: " << err; return; } if (!glew_version_2_1) // check machine supports 2.1 api. { qdebug() << "> glwidget::paintevent !!! scheme doesn't back upwards glew_version_2_1"; return; } /* setting texture , transfering info gpu */ static gluint texture = 0; if (texture != 0) { context->deletetexture(texture); } glpixelstorei(gl_unpack_alignment, 1); glbindtexture(gl_texture_rectangle_arb, texture); gltexparameteri(gl_texture_rectangle_arb, gl_texture_min_filter, gl_nearest); gltexparameteri(gl_texture_rectangle_arb, gl_texture_mag_filter, gl_nearest); gltexparameteri(gl_texture_rectangle_arb, gl_texture_wrap_s, gl_clamp_to_edge); gltexparameteri(gl_texture_rectangle_arb, gl_texture_wrap_t, gl_clamp_to_edge); glteximage2d(gl_texture_rectangle_arb, 0, gl_luminance, _frame->width(), _frame->height() + _frame->height() / 2, 0, gl_luminance, gl_unsigned_byte, _frame->bits()); assert(glgeterror() == gl_no_error); gltexenvi(gl_texture_env, gl_texture_env_mode, gl_modulate); glenable(gl_texture_rectangle_arb); glclearcolor(0.3, 0.3, 0.4, 1.0); int img_width = _frame->width(); int img_height = _frame->height(); int offset_x = 0; int offset_y = 0; glfloat gl_width = width(); // gl context size glfloat gl_height = height(); /* initialize shaders , execute them */ _init_shaders(); qdebug() << "paint(): gl_width:" << gl_width << " gl_height:" << gl_height << " img:" << _frame->width() << "x" << _frame->height(); int fill_mode = 1; switch (fill_mode) { case 0: // keepaspectratiobyexpanding { // need help! } break; case 1: // ignoreaspectratio { // nil special needs done operation. } break; case 2: // keepaspectratio default: { // compute aspect ratio , offset y widescreen borders double ratiox = img_width/gl_width; double ratioy = img_height/gl_height; if (ratiox > ratioy) { gl_height = qround(img_height / ratiox); offset_y = qround((height() - gl_height) / 2); gl_height += offset_y * 2; } else { gl_width = qround(img_width / ratioy); offset_x = qround((width() - gl_width) / 2); gl_width += offset_x * 2; } } break; } // mirroring texture coordinates flip image vertically glbegin(gl_quads); gltexcoord2f(0.0f, img_height); glvertex2f(offset_x, gl_height - offset_y); gltexcoord2f(img_width, img_height); glvertex2f(gl_width - offset_x, gl_height - offset_y); gltexcoord2f(img_width, 0.0f); glvertex2f(gl_width - offset_x, offset_y); gltexcoord2f(0.0f, 0.0f); glvertex2f(offset_x, offset_y); glend(); painter.endnativepainting(); } void glwidget::_init_shaders() { int f = glcreateshader(gl_fragment_shader); glshadersource(f, 1, &p_s_fragment_shader, 0); glcompileshader(f); _shader_program = glcreateprogram(); glattachshader(_shader_program, f); gllinkprogram(_shader_program); gluseprogram(_shader_program); gluniform1i(glgetuniformlocation(_shader_program, "tex"), 0); gluniform1f(glgetuniformlocation(_shader_program, "imgheight"), _frame->height()); gluniform1f(glgetuniformlocation(_shader_program, "chromaheight_half"), (_frame->height() / 2) / 2); gluniform1f(glgetuniformlocation(_shader_program, "chromawidth"), _frame->width() / 2); }

glwidget.h:

#include <qtopengl/qglwidget> #include <qtgui/qimage> #include <qpainter> class glwidget : public qglwidget { q_object public: glwidget(qwidget *parent = 0); ~glwidget(); void paintevent(qpaintevent *event); private: void _init_shaders(); bool _checkshader(int n_shader_object); qimage* _frame; int _shader_program; };

and here can download info file.

you can re-create "keep aspect ratio" branch (provided working), , flip ratio comparing sign, i.e.:

if (ratiox > ratioy)

becomes

if (ratiox <= ratioy)

but i'm not sure working (ratio calculations had bugged me - , yours tricky), , don't have qt atm can't try. should it. note image centered (not left-aligned on image), can fixed pretty easily.

edit:

here source code works in glut application (no qt, sorry):

static void drawobject(void) { int img_width = 1280;//_frame->width(); int img_height = 720;//_frame->height(); glfloat offset_x = -1; glfloat offset_y = -1; int p_viewport[4]; glgetintegerv(gl_viewport, p_viewport); // don't have qt :'( glfloat gl_width = p_viewport[2];//width(); // gl context size glfloat gl_height = p_viewport[3];//height(); int n_mode = 0; switch(n_mode) { case 0: // keepaspectratiobyexpanding { float ratioimg = float(img_width) / img_height; float ratioscreen = gl_width / gl_height; if(ratioimg < ratioscreen) { gl_width = 2; gl_height = 2 * ratioscreen / ratioimg; } else { gl_height = 2; gl_width = 2 / ratioscreen * ratioimg; } // calculate image size } break; case 1: // ignoreaspectratio gl_width = 2; gl_height = 2; // opengl normalized coordinates -1 +1 .. hence width (or height) = +1 - (-1) = 2 break; case 2: // keepaspectratio { float ratioimg = float(img_width) / img_height; float ratioscreen = gl_width / gl_height; if(ratioimg > ratioscreen) { gl_width = 2; gl_height = 2 * ratioscreen / ratioimg; } else { gl_height = 2; gl_width = 2 / ratioscreen * ratioimg; } // calculate image size offset_x = -1 + (2 - gl_width) * .5f; offset_y = -1 + (2 - gl_height) * .5f; // center on screen } break; } glmatrixmode(gl_projection); glloadidentity(); glmatrixmode( gl_modelview ); glloadidentity(); // simple ortho view, no fancy transform ... glbegin(gl_quads); gltexcoord2f(0, 0); glvertex2f(offset_x, offset_y); gltexcoord2f(imgwidth, 0); glvertex2f(offset_x + gl_width, offset_y); gltexcoord2f(imgwidth, imgheight); glvertex2f(offset_x + gl_width, offset_y + gl_height); gltexcoord2f(0, imgheight); glvertex2f(offset_x, offset_y + gl_height); glend(); // draw single quad }

this works comparing screen aspect ratio image aspect ratio. comparing ratios of image width screen width image height screen height. suspicious @ least, not wrong.

also, normalized opengl coordinates (provided simple orthogonal view) in range (-1, -1) lower-left corner (1, 1) upper right. means normalized width , height both 2, , offset (-1, -1). rest of code should self-explanatory. in case texture flipped (i tested kind of generic texture, not sure if upright), alter texture coordinates in respective direction (swap 0s imgwidth (or height) , vice versa).

edit2:

using pixel coordinates (not using normalized opengl coordinates) simpler. can use:

static void drawobject(void) { int img_width = 1280;//_frame->width(); int img_height = 720;//_frame->height(); glfloat offset_x = 0; glfloat offset_y = 0; int p_viewport[4]; glgetintegerv(gl_viewport, p_viewport); glfloat gl_width = p_viewport[2];//width(); // gl context size glfloat gl_height = p_viewport[3];//height(); int n_mode = 0; switch(n_mode) { case 0: // keepaspectratiobyexpanding { float ratioimg = float(img_width) / img_height; float ratioscreen = gl_width / gl_height; if(ratioimg < ratioscreen) gl_height = gl_width / ratioimg; else gl_width = gl_height * ratioimg; // calculate image size } break; case 1: // ignoreaspectratio break; case 2: // keepaspectratio { float ratioimg = float(img_width) / img_height; float ratioscreen = gl_width / gl_height; glfloat orig_width = gl_width; glfloat orig_height = gl_height; // remember able center quad on screen if(ratioimg > ratioscreen) gl_height = gl_width / ratioimg; else gl_width = gl_height * ratioimg; // calculate image size offset_x = 0 + (orig_width - gl_width) * .5f; offset_y = 0 + (orig_height - gl_height) * .5f; // center on screen } break; } glmatrixmode(gl_projection); glloadidentity(); glmatrixmode(gl_modelview); glloadidentity(); gltranslatef(-1, -1, 0); glscalef(2.0f / p_viewport[2], 2.0f / p_viewport[3], 1.0); // simple ortho view vertex coordinate pixel matching glbegin(gl_quads); gltexcoord2f(0, 0); glvertex2f(offset_x, offset_y); gltexcoord2f(img_width, 0); glvertex2f(offset_x + gl_width, offset_y); gltexcoord2f(img_width, img_height); glvertex2f(offset_x + gl_width, offset_y + gl_height); gltexcoord2f(0, img_height); glvertex2f(offset_x, offset_y + gl_height); glend(); // draw single quad }

note both versions of code utilize npot textures. adapt code fit object, 1 this:

void glwidget::paintevent(qpaintevent *event) { qpainter painter(this); painter.setrenderhint(qpainter::antialiasing); qdebug() << "> glwidget::paintevent opengl:" << ((painter.paintengine()->type() != qpaintengine::opengl && painter.paintengine()->type() != qpaintengine::opengl2) ? "disabled" : "enabled"); qglcontext* context = const_cast<qglcontext *>(qglcontext::currentcontext()); if (!context) { qdebug() << "> glwidget::paintevent !!! unable retrieve ogl context"; return; } context->makecurrent(); painter.fillrect(qrectf(qpoint(0, 0), qsize(1280, 768)), qt::black); painter.beginnativepainting(); /* initialize gl extensions */ glenum err = glewinit(); if (err != glew_ok) { qdebug() << "> glwidget::paintevent !!! glewinit failed with: " << err; return; } if (!glew_version_2_1) // check machine supports 2.1 api. { qdebug() << "> glwidget::paintevent !!! scheme doesn't back upwards glew_version_2_1"; return; } /* setting texture , transfering info gpu */ static gluint texture = 0; if (texture != 0) { context->deletetexture(texture); } glpixelstorei(gl_unpack_alignment, 1); glbindtexture(gl_texture_rectangle_arb, texture); gltexparameteri(gl_texture_rectangle_arb, gl_texture_min_filter, gl_nearest); gltexparameteri(gl_texture_rectangle_arb, gl_texture_mag_filter, gl_nearest); gltexparameteri(gl_texture_rectangle_arb, gl_texture_wrap_s, gl_clamp_to_edge); gltexparameteri(gl_texture_rectangle_arb, gl_texture_wrap_t, gl_clamp_to_edge); glteximage2d(gl_texture_rectangle_arb, 0, gl_luminance, _frame->width(), _frame->height() + _frame->height() / 2, 0, gl_luminance, gl_unsigned_byte, _frame->bits()); assert(glgeterror() == gl_no_error); gltexenvi(gl_texture_env, gl_texture_env_mode, gl_modulate); glenable(gl_texture_rectangle_arb); glclearcolor(0.3, 0.3, 0.4, 1.0); /* initialize shaders , execute them */ _init_shaders(); int img_width = _frame->width(); int img_height = _frame->height(); glfloat offset_x = 0; glfloat offset_y = 0; glfloat gl_width = width(); // gl context size glfloat gl_height = height(); qdebug() << "paint(): gl_width:" << gl_width << " gl_height:" << gl_height << " img:" << _frame->width() << "x" << _frame->height(); int fill_mode = 0; switch(fill_mode) { case 0: // keepaspectratiobyexpanding { float ratioimg = float(img_width) / img_height; float ratioscreen = gl_width / gl_height; if(ratioimg < ratioscreen) gl_height = gl_width / ratioimg; else gl_width = gl_height * ratioimg; // calculate image size } break; case 1: // ignoreaspectratio break; case 2: // keepaspectratio { float ratioimg = float(img_width) / img_height; float ratioscreen = gl_width / gl_height; glfloat orig_width = gl_width; glfloat orig_height = gl_height; // remember able center quad on screen if(ratioimg > ratioscreen) gl_height = gl_width / ratioimg; else gl_width = gl_height * ratioimg; // calculate image size offset_x = 0 + (orig_width - gl_width) * .5f; offset_y = 0 + (orig_height - gl_height) * .5f; // center on screen } break; } gldisable(gl_cull_face); // might cause problems if enabled glbegin(gl_quads); gltexcoord2f(0, 0); glvertex2f(offset_x, offset_y); gltexcoord2f(img_width, 0); glvertex2f(offset_x + gl_width, offset_y); gltexcoord2f(img_width, img_height); glvertex2f(offset_x + gl_width, offset_y + gl_height); gltexcoord2f(0, img_height); glvertex2f(offset_x, offset_y + gl_height); glend(); // draw single quad painter.endnativepainting(); }

can't guarantee lastly code snippet error-free since don't have qt. in case there typos, should rather straightforward prepare them.

c++ c qt opengl image-scaling

Comments

Popular posts from this blog

How do I check if an insert was successful with MySQLdb in Python? -

delphi - blogger via idHTTP : error 400 bad request -

postgresql - ERROR: operator is not unique: unknown + unknown -