Saturday, November 27, 2010

TracExplorer and VS 2010 Integration

TracExplorer and VS 2010 Integration

(Nov 27, 2010)

Introduction

Caveat: I'm new to an issue tracker system.  How I use Trac and TracExplorer, as described here, may not be a good practice, but it worked.
(you can skip the introduction if you already know Trac and TracExplorer well)

Trac is an interesting issue tracking system.  Its default feature set provides what many software developers need.  Trac's plugin architecture also allows us to add more features to the system if we need them.  Therefore, default Trac is useful and relatively simple.  Extended Trac is more powerful, but may be complicated.  Using Trac inside the Visual Studio can also be convenient, provided that the integration provides adequate features and user interfaces.

TracExplorer has a fascinating idea for VS 2005 and 2008 integration.  However, the available binary in source forge does not work with VS 2010.  In addition, TracExplorer (as of version 0.20 beta) is not feature rich.  It is rather inadequate and not improved for months.  For example, TracExplorer does not allow the user to create a new ticket.  A typical web-based user interface is needed.  Yet, it allows us to quickly view and modify tickets.  Many people may be satisfied with the available features.

This integration contains two parts: installatiion of Trac's XML-RPC plugin, which is a requirement of TracExplorer, and VS 2010 integration.

Installation of XML-RPC plugin

  1. Get the plugin by downloading its source package.  Don't worry that you do not get the binary. The actual installation is simple.

  2. Extract the source package to a preferred folder.  Inside the package, we mainly use the source inside the trunk.  Other folders usually contain outdated version provided for compatibility.

  3. Generate an 'egg' package. This step requires Python, but we should already have it installed as Trac's prerequisite.  A default Python's folder from BitNami is "C:\Program Files\BitNami Trac Stack\python\python."
    Therefore, in the command prompt of the trunk folder, type

    "C:\Program Files\BitNami Trac Stack\python\python" setup.py bdist_egg  [Note: the double qoutes are needed to dealing with a path with space.]

  4. The necessary outputs is in the newly created 'dist' folder.  Copy all *.egg files (typically, there is only one egg file for this plugin) from there to Trac's plugin folder.  An example of the plugin folder is
    "C:\Users\username\BitNami Trac Stack projects\InitialProject\plugins"

  5. change trac.ini of a project that wants to use the plugin by including lines
    [components]
    tracrpc.* = enabled

    Note that if the [components] section already exists, there is no need to create a new one.  Just append 'tracrpc.* = enabled' to the section.

  6. Restart the apache service.  It is important to note that the service name is tracApache if you use BitNami.  If you need to run this service from a command line, use
    "C:\Program Files\BitNami Trac Stack\apache2\bin\httpd.exe" -k runservice

If you want to install this plugin for every project, try using easy_install descriped in Trac website.

Visual Studio 2010 Integration

This section is probably why you come to this page.  You may think that you need an installer for integration, but it is not necessary at all.  All you need are compatible add-in binaries and add-in description file.

  1. Download the package I built from this link.  This package includes binaries and description file.  If you are interested in how I prepared the package, see the next section. 

  2. Unpack it to your preferred folder.  No need to bother with "Program Files".  Any folder will do.

  3. Open VS 2010.

  4. Go to 'Tools->Options.'

  5. In the 'Add-in/Macros Security' in the 'Environment' node.  Then, 'Add..' the path to the folder in you chose in Step 2.

  6. Close and reopen VS 2010.

  7. Verify add-in installation: (1) Go to 'Tools->Add-inManager'.  You should see TracExplorer.VSTrac listed there; (2) By clicking 'View' in the menu bar, you should see 'Trac Explorer.'

Preparation of Binary Package

This part is for those who are curious about how I built the add-in from the source code provided in SourceForge.net.

Note: I do not modify any source code.  I just rebuild the binary and add a proper add-in description file.


  1. Install TortoiseSVN in its default folder.  You need TortoiseSVN, as the build script uses a file from TortoiseSVN.  The file is specified by the default path of TortoiseSVN. If you already installed it elsewhere, see note at the end of this section.

  2. Check out the source tree from SourceForge.  The SVN command is 'svn co https://vstrac.svn.sourceforge.net/svnroot/vstrac vstrac.'  If you use TortoiseSVN to do this, just enter the URL https://vstrac.svn.sourceforge.net/svnroot/vstrac.

    Do not use GNU tarball, as the build script needs to obtain some information of the SVN source tree.  This information is automatically available if you use SVN to check out the source tree.

  3. Go to the 'trunk/src' folder.  Use VS 2010 to open TracExplorer2008.sln.

  4. Rebuild the solution in a release mode.  Whatever you try to do, you will get x86 binaries compatible with VS 2010 in the 'trunk/bin' folder.

Note: What if I installed TortoiseSVN in a non-default foder?
In this case, version_scm.bat in the 'build' folder will not work.  You, however, can specify your TortoiseSVN path by modifying the batch file.

Trivia: In SourceForge, this software is called TracExplorer, but in Visual Studio's view, it becomes Trac Explorer (with space added).

Hope this helps,
Pinyo

Friday, November 12, 2010

Building 64-bit Qt 4.7 using MinGW-w64

Building 64-bit Qt 4.7 using MinGW-w64

(Nov 12, 2010)

I successfully built 64-bit Qt libraries using MinGW-w64.  Although I had to change Qt source code, it was only one line of code in the current source tree of qt-mingw-w64.  The steps I took to built 64-bit Qt are described below.  Note that my explanation is a bit verbose, as those who just start using MinGW or Qt may need additional information.

  1. Obtain Qt 4.7 source code.
    • Go to http://gitorious.org/+qt-mingw-w64/qt/qt-mingw-w64-qt
    • Click on the 'Source tree' button.  Note that you can directly go to this link.  However, go to the link above allows you to read recent activities of Qt development that may related to you.
    • On the right side, you see several 'branches'.  As of Nov 12, 2010, the one you want to use is plain '4.7,' which already merges Webkit facility.
    • Once you click on the '4.7' button, you probably need to wait a minute or two.  This allows the server to create tar.gz for downloading.  Basically, the package is prepared on the fly.  This is how gitorious works.

  2. Unpack the source tree to your preferred folder.  For this note, I unpacked it to 'F:\Qt\qt-4.7.1_mingw_w64.'  This path is used here for a demonstration purpose.

  3. Fix file 'src\corelib\tools\qsimd.cpp' by changing line 289 from 'long tmp' to 'quint64 tmp,' as suggested by Jonathan Liu [ref].  See the ref for more information if you don't understand what I meant.  Please note that if you get a newer Qt version, this problem may already be solved.

  4. Get MinGW-w64 from sourceforge.  I use the native compiler for Windows 64-bit.  FYI: MinGW offers a cross-compilation feature.  You can use Linux or 32-bit Windows to produce 64-bit Windows binary.
    • Go to http://sourceforge.net/projects/mingw-w64/files/
    • Click on Section 'Toolchains targetting Win64'
    • Pick the release you like.  I suggest you use personal build from sezero, as sezero usually adds some patches or features that you might want to use (e.g., updated OpenGL and OpenCL headers).  I employed mingw-w64-bin_x86_64-ming_20101003_sezero.zip.  By the time you get MinGW from sourceforge, the build may be gone.  Yet, you are supposed to get a newer version than mine.
    • If you wonder what are inside each sezero build see his note in Section 'Toolchains targetting Win64 -> Personal Builds -> sezero_20101003.'  sezero did a good job in bookkeeping (at least it seemed).

    • Personal build of rubenvb is probably an interesting choice, as he appears to be a Qt contributor.  I believe that his MinGW build should not have a serious issue with Qt compilation.

  5. Unpack MinGW to a preferred place.  For this note, it is 'D:\mingw_w64_sezero_oct_3_2010.'

  6. Go to D:\mingw_w64_sezero_oct_3_2010\bin and change gmake.exe to mingw32-make.exe.  Note that it is MinGW's gmake not Qt's qmake.  I backed up original mingw32-make.exe to other file first, although they might be exactly the same binary in the sezero's build.

  7. Download Perl.  Yes, you need Perl to syncqt during compilation.  I suggest ActivePerl Community Edition.  If you want to repeat what I did here, download Windows 64-bit version with an MSI installer.  I did not like Strawberry Perl, as I encountered many issues with it during past summer, especially the fact that it comes with its own GCC.  Its GCC confused me about GCC version since both Strawberry's and MinGW's GCC showed up in the path.  These issues may be already resolved by now, though.

  8. Install Perl.  What a simple task if you have admin right to your machine.  If you don't, see steps below. 
    • Download AS Package (ZIP).
    • Unpack and set path to 'base_perl_path/bin' and 'base_perl_path/site/bin.'
    • If you want a temporary session path for your command prompt (cmd.exe), type 'set PATH=C:\Perl64\bin;C:\Perl64\site\bin;%PATH%, provided that you unpack its main contents to C:\Perl64 and discard its 'support' folder.  Don't close this command prompt yet because your path settings are temporary.  You need to use it until the end.

  9. Now, you have all you need to embark the actual compilation phase. Set path to your MinGW folder.  For example, 'set PATH=D:\mingw_w64_sezero_oct_3_2010\bin;%PATH%.'
    If you want to know what your current path is, type 'echo %PATH%.'

  10. Set QTDIR variable by typing 'set QTDIR=F:\Qt\qt-4.7.1_mingw_w64\bin.'

  11. Go to Qt base folder (F:\Qt\qt-4.7.1_mingw_w64) and run configure from the command prompt.  You can choose any configure options, perhaps except phonon (my Qt build did not have phonon).  I used 'plain' configure that generated everything, but you might want to get rid of many things, especially qt3 support, examples, and demos.

    Update (Feb 18, 2011): it is easy to build Qt with phonon.  The option, however, is off by default for MinGW.  I tested with
    configure -opensource -phonon -no-qt3support -nomake example -nomake demos
    Although I did not test if the created libraries work or not, things are built smoothly.


  12. Type 'gmake.'  Note that if you want to try multi-threaded compilation, type 'gmake -jx,' where x is the number of threads.  For instance, 'gmake -j2' for a dual-core machine, 'gmake -j4' for a quad-core machine, and 'gmake -j6' for a six-core machine (if you want to put every core to work).  I, however, just used gmake with no option since I wanted to simplify everything for this build.

  13. Wait with patience.

The following summarizes downloads and tweaks I made during the above steps.  Basically, we need to do only two things in addition to what most people do with mingw 32-bit.  I have not try building phonon yet (and I think I don't need it for a long time).

Download summary

  1. Qt 4.7 mingw-w64 branch (~ 130 MB).  See step 1.
  2. MinGW-w64 (~ 55 MB).  See step 4.
  3. ActivePerl (~ 24 MB).  See step 7.

Tweak summary

  1. Change source code (1 file, 1 line).  See step 3.
  2. Change gmake to mingw32-make.exe.  See step 6.  (May not be necessary any more.)


Hope this helps,
Pinyo Taeprasartsit

Additional reference: mingw-w64 mail archive


I welcome questions and comments.  Feel free to ask and share if you have any.

Friday, August 06, 2010

VTK Installation on Windows 64-bit for Visual Studio

VTK Installation on Windows 64-bit for Development in Visual Studio

VTK installation in Windows 32-bit is typically painless, as the platform was already tested by many developers and became mature.  (Yet, if you have a problem with it, the troubleshooting note at the end of this page might help.)  For Windows and VTK 64-bit, however, we may encounter a few issues.  This note describes what I did to install VTK 5.6 on my MS Windows 7 Home Premium 64-bit.  This note should be applicable to those who need to do the same thing.

The first part is for installation with default VTK build options.  These options are VTK_USE_CHARTS, VTK_USE_GEOVIS, VTK_USE_INFOVIS, VTK_USE_N_WAY_ARRAYS, VTK_USE_RENDERING, and VTK_USE_VIEWS.
  1. Download VTK 5.6 source from http://vtk.org/VTK/resources/software.html.
  2. Extract it to create a source folder.
  3. Open CMake (I used 2.8.1, and it is supposed to work with the most recent version at cmake.org).
  4. In CMake, choose a source and binary folders (Figure 1).


    Figure 1.  CMake 2.8.1 after source code and binary folder selected.

  5. Click the 'Configure' button.
  6. CMake asks about generator (compiler) we want to use (Figure 2).  The one we want to use is Visual Studio 9 2008 Win64 (the one without suffix Win64 is 32-bit) with 'Use default native compiler'.  We can also use Visual Studio 10 Win64 if a target development platform is Visual Studio 10.  Using MinGW will generate incompatible binary for Visual Studio development.  In addition, 64-bit MinGW is quite tricky to use as of Aug 2010.


    Figure 2. 'Choose compiler' dialog

  7. If initial configuration goes fine, there will be no critical errors and VTK build options will be available for us to choose (Figure 3).

    Figure 3.  Build options for VTK after the initial configuration.  Red regions in CMake highlight new options since the previous configuration.  As this is the first time, every option is new and highlighted.

  8. I used the default options.  This option builds a static libraries, which is what I prefer.
  9. I think options VTK_USE_PARALLEL may help speed up processing in a multi-core system, but I need to learn more about it.  Also, VTK_USE_QT is probably good for me, as I have used Qt a lot recently.  Yet, I need to know what it does first.
  10. Click the 'Configure' button again when options are settled.
  11. Click the 'Generate' button to generate a proper visual studio solution tree.
  12. Go to the output folder specified earlier, you should see 'VTK.sln'.
  13. Open the solution and 'Build Solution'.  It will take a while to finish (around 5-10 minutes).  Note that if you only want to use VTK and do not intend to debug into its source code, it is recommend to build it in the release mode.
  14. In the solution tree, right click on the 'INSTALL' project and 'Build'.  Update Dec 15, 2011: some systems may experience a build error due to write permission to C:\Program Files (x86).  This depends on your Windows settings.  One way to solve this issue is to close Visual Studio and run it 'as administrator' (credit to Alex Southern, see his comment below).
  15. The outputs are kept in C:\Program Files (x86)\VTK.  Both include and lib folders are what we are going to use in further software development.
  16. If you want to build both 32- and 64-bit packages, move the outputs to another folder and repeat the process.  Only the generater in step 6 is needed to change.  We have to move the current outputs because new outputs will be saved to the same folder.

Build VTK with parallel-processing and Qt-support options

Next, I will discuss installation of VTK with additional options for Visual Studio 2008.  These options are VTK_USE_QT and VTK_USE_PARALLEL.  We can jump to step 7 discussed in the last section. At this point, we only need to check the QT and PARALLEL options and then, hit the 'Configure' button the project again.  CMake will configure the project and comes up with two new options highlighted in red (Figure 4).  Once you set QT_QMAKE_EXECUTABLE to your target version, hit the 'Configure' button for the third time.  If every thing goes fine, you will get a refreshed dialog with message "Configuring done", as depicted in Figure 5.  There are chances that you did not succeed or get a wrong Qt version.  If the latter is your problem, read the caption of Figure 4.  If it fails for other reason, please see the troubleshooting note at the end of this document.  I hope it has what you need.

The rest of the process is generally the same.  If you encounter linking errors when you build VTK in Visual Studio, see Topic 2 in the troubleshooting note provided below.


Figure 4.  CMake after the second configuration with QT and PARALLEL.  QT_QMAKE_EXECUTABLE is selected by what you do with Qt integration for Visual Studio.  In other words, CMake checks the path of default Qt version you set in Visual Studio and use it here.  In fact, CMake and VTK build script strictly use the default Qt version.  You cannot effectively change Qt version here, as it will be eventually reset to the VS default version. Therefore, you may need to open VS and change Qt default version to set QT_QMAKE_EXECUTABLE.


Figure 5. A successful configure dialog.

Troubleshooting Note for Qt Option

1. Problem: "I specified qmake path correctly, but it seems that CMake looks for qmake in a wrong path."

Solution:
You may set QT_QMAKE_EXECUTABLE to point to qmake correctly, but this problem may arise since CMAKE will use outdated qmake properties, such as QT_INSTALL_PREFIX and QT_INSTALL_BINS.  For example, you build Qt in one path and later on you move it to another.  The properties, however, is fixed at the time you build Qt.  You can confirm this issue by going to qmake's make folder and using the command prompt to type 'qmake -query' to see all qmake properties.

If the issue is confirmed, you need to compile qmake again (not the whole Qt library set).  Alternatively, you can just move or copy your Qt directory to the old place to resolve the issue.  If moving or copying Qt folder is not an option, you can quickly re-compile qmake by
  • Rename the bin and qmake folders of Qt to something else.
  • Copy the pristine bin and qmake folders from the Qt source to the Qt folder.
  • Configure Qt from Visual Studio command prompt.  For example, by using 'configure -platform win32-msvc2008 -release -static -opensource -nomake demo -nomake examples -no-qt3support -fast'.  You probably did this before when you compiled Qt libraries.
  • Copy things from the old bin and qmake folders to your current bin and qmake folders, but make sure you do not replace the new qmake you just compiled.
  • Now, you have qmake with updated qmake properties.  I hope you can configure VTK in CMake as usual from this point.


2. Problem: "I got linking errors when I build VTK in Visual Studio.  It complained about PlaySoundW, WSAAsyncSelect, and many more."

Solution:
Based on my experience, this problem is platform specific.  You may encounter this issue if you use static library version of Qt and the problems in 32-bit and 64-bit platforms are not the same.  These linking problems, however, are related to Windows libraries and occurs when your VTK build script does not correctly include them.

Fortunately, you can include corresponding libraries yourself with ease by following the guidelines described below.
  • Identify problematic projects.  Potential problematic projects are QVTKWidgetPlugin, QVTK, QtChartCxxTests, ChartCxxTests, and QVTKCxxTests.
  • Search the Internet (or use your prior knowledge, or your MSDN documents) to identify corresponding libraries.  For example, the corresponding library of PlaySoundW is Winmm.lib and that of WSAAsyncSelect is Ws2_32.lib.
  • Add the libraries to the Visual Studio project properties, as you do normally.  Basically, you can right click the project in the Solution Explorer of Visual Studio and select 'Properties'.  Then, go to node 'Configuration Properties -> Linker -> Input' and insert the library name to 'Additional Dependencies'.


3. Problem: I checked 'VTK_WRAP_PYTHON' and CMake errors occurred.  It complaint about PYTHON_INCLUDE_DIR AND PYTHON_LIBRARY.

The error message looked like:
CMake Error: The following variables are used in this project, but they are set to NOTFOUND.

Please set them or make sure they are set and tested correctly in the CMake files:

PYTHON_INCLUDE_DIR

PYTHON_LIBRARY

Solution:
The two CMake variables seem to come and go and not consistent over VTK versions.  You might want to add them yourself to CMakeLists.txt.
First, locate IF(VTK_WRAP_PYTHON).  Then, modify the section to something like

IF(VTK_WRAP_PYTHON)
  SET(VTK_LANGUAGES ${VTK_LANGUAGES} PYTHON)
  #Addition by Pinyo
  SET(PYTHON_INCLUDE_DIR "C:/Python31/include")
  SET(PYTHON_LIBRARY "C:/Python31/libs/python31.lib")
ENDIF(VTK_WRAP_PYTHON)

Please note that PYTHON_LIBRARY is the actual library file, not just a folder (although you sometimes see the term python libraries and think that it is a lot of libraries, not a single file.).
Finally, configure and generate make files as usual.


4. Problem: I used VS 2010 64-bit and CMake 2.8.3 and got Q4VTKWidgetPlugin.h(57): error : Undefined interface.  This corresponds to line Q_INTERFACES(QDesignerCustomWidgetCollectionInterface).

Solution
:
This is, in fact, a CMake problem.  To be exact, the QT_QTDESIGNER_INCLUDE_DIR is not set correctly.  An example of the value is include/QtDesigner/.  This option is available if we want to generate MinGW Makefiles.  This option also was automatically set in the past and it was correct.  For an unknown reason, it does not work on some machines (VTK 5.6.1 as of Feb 18, 2011).

To solve the problem, go to folder GUISupport\Qt and open CMakeLists.txt.  Then, add a line
SET (QT_QTDESIGNER_INCLUDE_DIR "G:/Workspace/Qt/qt-release-4.7.1-vs2010/include/QtDesigner")
Make sure you adjust the ad hoc path to what suits your computer.

The above line should be added right after FIND_PATH(QT_QTDESIGNER_INCLUDE_DIR QDesignerComponents ...), as the function FIND_PATH may not work correctly in your build.

Hope this helps,
Pinyo Taeprasartsit
(Aug 2010, Feb 2011)


Saturday, June 12, 2010

GL_TEXTURE_2D

GL_TEXTURE_2D

In this part, we continue with a convenient and powerful method for 2D OpenGL, 2D texture.
The strength of using OpenGL 2D texture over glDrawPixels is that linear filtering can be selected.
Moreover, the task is systematic and not tedious.

I, however, do not know if using RGBA is better in terms of performance or not, but I used it in this example.
Thus, we have to modify a way to create a texture image.  The A element can be set to 255, as shown below.

texImage[nColorBytes*(h*m_nWidth + w) + 3] = (GLubyte) 255;

To initialize 2D texture with linear filtering, the following code can be used to make a texImage an OpenGL texture.

void GLTexture2D::initializeGL()
{
    // Basic initialization
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glShadeModel(GL_FLAT);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    // Texture initialization
    glGenTextures(1, &textureName);
    glBindTexture(GL_TEXTURE_2D, textureName);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);    // GL_NEAREST is another choice
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_nWidth, m_nHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, texImage);
}



To draw a 2D image as a texture, we have to map image coordinates to texture coordinates when we draw GL_QUADS.
In the following function, we map the texture created in initializeGL to rectangular coordinates -1.0 to 1.0.

void GLTexture2D::paintGL() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_TEXTURE_2D);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    glBindTexture(GL_TEXTURE_2D, textureName);

    glBegin(GL_QUADS);
        glTexCoord2f(0.0, 0.0);    glVertex3f(-1.0, -1.0, 0.0);
        glTexCoord2f(0.0, 1.0);    glVertex3f(-1.0,  1.0, 0.0);
        glTexCoord2f(1.0, 1.0);    glVertex3f( 1.0,  1.0, 0.0);
        glTexCoord2f(1.0, 0.0);    glVertex3f( 1.0, -1.0, 0.0);
    glEnd();
    glDisable(GL_TEXTURE_2D);
}


===================================

An example project for Eclipse CDT / Qt on Linux is stored at a public folder here.
The project file is valid for Eclipse CDT on Linux with Qt integration (Ubuntu 9.04 Juanty), but the main part should be applicable to most C++ environment.


Pinyo Taeprasartsit
Octobor 2009

glDrawPixels + Qt (Part 3)

glDrawPixels + Qt (Part 3)

In this part, we will discuss the use of a pixel buffer in OpenGL.  A pixel buffer is an advanced feature, which is aimed at increasing screen draw rate.
For OpenGL 2.1, this pixel buffer is a part of a standard extension and not in OpenGL core. 

To use this extension, things become much more complicated, but can still be explained step by step, as follows.
  1. We need to include <GL/glew.h> before any other OpenGL context.  In Qt, we can normally include it in a header file before we include any QGLWidget.
    For your information, this extension is OpenGL Extension Wrangler Library (GLEW).

  2. Since GLEW is a library, we have to declare its use in a .pro file.  Open a .pro file and add a line

    LIBS    += -lGLEW


    Your project can now link to this library properly.

  3. Initialize GLEW before any other OpenGL work.  This should be done in the initializeGL method of QGLWidget.
    Initializing GLEW, however, may fail, especially if we try to initialize it for the second time.
    Thus, it is recommended to prepare for initialization error, as shown below.

    GLenum initStatus = glewInit();
    if (initStatus != GLEW_OK) {
        cout << "Cannot initialize glew." << endl;
        exit(1);
    } else
        cout << "glew is successfully initialized." << endl;


  4. Bind data to a buffer.  We need to provide a non-local GLuint to be employed as a buffer ID.  This may be a class-member or global variable.
    Assume that we declare the ID variable as a class member by

    GLuint pixelBuffer;

    Next, bind the buffer to it.  Note that chkImage contains pixel data and must be already filled.
    These patch of code is still in initializeGL.

    glGenBuffers(1, &pixelBuffer);
    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pixelBuffer);
    glBufferData(GL_PIXEL_UNPACK_BUFFER, 3* m_nWidth * m_nHeight, chkImage,
            GL_STATIC_DRAW);

  5. Draw pixels in paintGL by

    GLubyte* nBuffOffset = 0;
    glDrawPixels(m_nWidth, m_nHeight, GL_RGB, GL_UNSIGNED_BYTE, nBuffOffset);


    Note that instead of providing a pointer to chkImage, we provide an offset in a buffer.  The zero offset means that we are going to use the first byte of the buffer and so on.

    To make this matter more understandable in terms of actual implementation, methods for initialization and painting are shown below.

    void GLImage2_2::initializeGL()
    {
        GLenum initStatus = glewInit();
        if (initStatus != GLEW_OK) {
            cout << "Cannot initialize glew." << endl;
            exit(1);
        } else
            cout << "glew is successfully initialized." << endl;

        // Set coordinate system
        glOrtho(0, m_nWidth, 0, m_nHeight, 0, 1);

        // Basic initialization
        glClearColor(0.0, 0.0, 0.0, 0.0);
        glShadeModel(GL_FLAT);
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

        // Using buffer objects
        glGenBuffers(1, &pixelBuffer);
        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pixelBuffer);
        glBufferData(GL_PIXEL_UNPACK_BUFFER, 3* m_nWidth * m_nHeight, chkImage,
                GL_STATIC_DRAW);
    }




    void GLImage2_2::paintGL() {
        glClear(GL_COLOR_BUFFER_BIT);
        glRasterPos2i(0, 0);

        GLubyte* nBuffOffset = 0;
        glDrawPixels(m_nWidth, m_nHeight, GL_RGB, GL_UNSIGNED_BYTE, nBuffOffset);
    }


So, what if we have several buffers?  In that case, we have to prepare more buffer-ID variables, bind them, and populate them. 
At the painting time, we specify our target buffer.  For concreateness, see the code below.

void GLImage2_2::paintGL() {
    glClear(GL_COLOR_BUFFER_BIT);
    glRasterPos2i(0, 0);

    if (target = 0)
        
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pixelBuffer1);
    else
        
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pixelBuffer2);

    GLubyte* nBuffOffset = 0;
    glDrawPixels(m_nWidth, m_nHeight, GL_RGB, GL_UNSIGNED_BYTE, nBuffOffset);
}

===================================

An example Eclipse CDT / Qt project is stored at a public folder here.
The project file is valid for Eclipse CDT on Linux with Qt integration (Ubuntu 9.04 Juanty), but the main part should be applicable to most C++ environment.


Pinyo Taeprasartsit
September 2009

glDrawPixels + Qt (Part 2)

glDrawPixels + Qt (Part 2)

In this part, we are going to use Qt to load an image from a file and display it with QGLWidget by using glDrawPixels.
The process is generally the same, except the order of function calls and the use of Qt to load an image file.

First, take a look at the constructor

GLImage2_1::GLImage2_1(QGLWidget *parent)
    : QGLWidget(parent)
{
    makeImage();
    m_nCurrWidth = m_nWidth;
    m_nCurrHeight = m_nHeight;
}


The function makeImage() is called early because we want to know image dimensions and give a correct size hint to a QGLWidget.
initializeGL is, therefore, leaner.

void GLImage2_1::initializeGL()
{
    // Set coordinate system
    glOrtho(0, m_nWidth, 0, m_nHeight, 0, 1);

    // Basic initialization
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glShadeModel(GL_FLAT);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
}


Now, let's get to makeImage(), the most important part of this article.

void GLImage2_1::makeImage() {
    string fileName("/home/pinyo/temp/beach.jpg");

    QImage qImage;
    qImage.load(QString(fileName.c_str()));

    m_nWidth = qImage.width();
    m_nHeight = qImage.height();

    chkImage = new GLubyte[m_nHeight * m_nWidth * 3];

    for (int h = 0; h < m_nHeight; ++h) {
        int nImgH = m_nHeight - h - 1;

        for (int w = 0; w < m_nWidth; ++w) {
            QRgb color = qImage.pixel(w, nImgH);

            chkImage[3* (h *m_nWidth + w) + 0] = (GLubyte) qRed(color);
            chkImage[3*(h*m_nWidth + w) + 1] = (GLubyte) qGreen(color);
            chkImage[3*(h*m_nWidth + w) + 2] = (GLubyte) qBlue(color);
        }
    }

    return;
}

There are a few things we have to pay attention to.
  1. QImage is used and (declared in qimage.h)
  2. We have no need to explicitly supply the image file type to its load function.  Qt first relies on file extension (.jpg, .png, for example).  If it fails or there is no file extension, Qt will make a guess based on file header.
  3. Notice the use of int nImgH = m_nHeight - h - 1;  This is essential because the image coordinate system in OpenGL is different from most computer imaging systems, including Qt.  In Qt the origin is at top left and the y-axis points downward, while in OpenGL, the origin is at bottom left and the y-axis points upward.  Since the x-axes in Qt and OpenGL are the same, there is no need to convert x-coordinates.


========================================

An example Eclipse CDT / Qt project is stored at a public folder here.
The project file is valid for Eclipse CDT on Linux with Qt integration (Ubuntu 9.04 Juanty), but the main part should be applicable to most C++ environment.


Pinyo Taeprasartsit
September 2009

glDrawPixels + Qt (Part 1)

glDrawPixels + Qt (Part 1)

This is an article following GL_POINTS + Qt.  As mentioned earlier, we can use glDrawPixels with glPixelZoom to deal with 2D graphics.
Although it appears to be more complicated than GL_POINTS, glDrawPixels is still another simple method.

The strength of glDrawPixels is its ability to scale an image in a way most people expect.  For example, a pixel in the original scale may occupy more than one pixel in a window, if the window is scaled up.
We can also increase draw rate by using buffer, but Part 1 will not cover that topic.

Now, let's see how to draw an image by using glDrawPixels and glPixelZoom.  Recall that the coordinate system of OpenGL may be different from most imaging systems.  OpenGL set the origin at the bottom left by default.

The major difference between GL_POINTS and glDrawPixels is that GL_POINTS is based on a call list, while glDrawPixels is based on an array.
This array is a flatten 3D or 4D array.  Namely, RGB(A) data is kept together in a 1D array.  We have to tell OpenGL a color model we want to use.

Assume that we want to use the RGB color model and the image is of size 128x128 pixels.  Therefore, we have to create a 1D array of size 128x128x3.  RGB values of one pixels are packed at contiguous array cells, as shown below.

/// Build a checker board, each grid cell is of size 16 x 16 pixels.
///
0x10 is a base-16 number equal to 16.
void GLImage::makeImage() {
    chkImage = new GLubyte[m_nHeight * m_nWidth * 3];

    int c;
    for (int h = 0; h < m_nHeight; ++h)
        for (int w = 0; w < m_nWidth; ++w) {
            c = ( ((h&0x10) == 0)^((w&0x10) == 0) )*255;
            chkImage[3*(h*m_nWidth + w) + 0] = (GLubyte) c;
            chkImage[3*(h*m_nWidth + w) + 1] = (GLubyte) c;
            chkImage[3*(h*m_nWidth + w) + 2] = (GLubyte) c;
        }

    return;
}


The drawing process is super easy.

void GLImage::paintGL() {
    glClear(GL_COLOR_BUFFER_BIT);
    glRasterPos2i(0, 0);
    glDrawPixels(m_nWidth, m_nHeight, GL_RGB, GL_UNSIGNED_BYTE, chkImage);
}

The resize process needs to specify zoom factors for both x- and y-axes.  Note that we may not need to use m_nCurrWidth and m_nCurrHeight, if we do not intend to use them for other proposed.

void GLImage::resizeGL(int width, int height) {
    glViewport(0, 0, width, height);
    m_nCurrWidth = width;
    m_nCurrHeight = height;

    glPixelZoom(m_nCurrWidth / (double) m_nWidth, m_nCurrHeight / (double) m_nHeight);
}


The way for initialization may be a bit more complicated than usual, but it is a pattern we can just follow (somewhat blindly).

void GLImage::initializeGL()
{
    glClearColor(0.0, 0.0, 0.0, 0.0);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, m_nWidth, 0, m_nHeight, 0, 1);
    glMatrixMode(GL_MODELVIEW);

    glShadeModel(GL_FLAT);
    makeImage();
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
}


Notice the use of glShadeModel(GL_FLAT).  glPixelStorei is employed to specify the way we store color data.  The parameter 1 indicates that it is 1-btye alignment.  Basically, there is no restriction of the beginning position of each row with this parameter.  If it is 2, each row must start on even-numbered byte.


========================================

An example Eclipse CDT / Qt project is stored at a public folder here.
The project file is valid for Eclipse CDT on Linux with Qt integration (Ubuntu 9.04 Juanty), but the main part should be applicable to most C++ environment.


Pinyo Taeprasartsit
September 2009

Tuesday, May 25, 2010

Eclise + MinGW + FreeGlut (2)

Eclipse CDT + MinGW + FreeGlut (2)

This tutorial starts with building freeglut to create a simple working application with Eclipse and MinGW in both 32-bit and 64-bit versions.  This tutorial refers to a few other tutorials so that readers can jump to the part they want to know conveniently.  Follow the instructions below is relatively a simple task.

  1. The first task is to obtain Eclipse CDT.  This tutorial is based on Eclipse CDT Galieo (SR2 Build ID:2010 02 18-1602).  You can download it from this link, but the most recent version on the Eclipse server may be your preferred choice.

  2. Obtain your preferred MinGW version.  I prepared 32-bit MinGW 4.5.0 and 64-bit MinGW 4.6.0 (2010-April-16 Experimental) packages for you to download.  Note that the include paths within the packages are different.  This is the default settings of the two packages.  Eclipse, however, knows the 32-bit folder paths well, but does not aware of the include paths for the 64-bit version.  Probably, this is because the 64-bit version is still experimental.

  3. Download freeglut.  This tutorial uses freeglut 2.6.0.  If you want to use a pre-compiled binary package, try the following links.
    1. freeglut 32-bit, compiled with 32-bit MinGW 4.5.0 (O2 optimization).  libfreeglut32.a and freeglut32.dll are for dynamic linking, while libfreeglut32_static.a is for static linking.  Header files are included.  I built this package myself.  Feel free to contact me if you have any problem (use the comment in my blog)

    2. freeglut 64-bit, compiled with 64-bit MinGW 4.6.0 (2010-April-16 Experimental) (O2 optimization).  libfreeglut64.a and freeglut64.dll are for dynamic linking, while libfreeglut64_static.a is for static linking.  Header files are included.  I built this package myself.  Feel free to contact me if you have any problem (use the comment in my blog)

    3. From other source

  4. Build freeglut (If you get the binary package from Step 3, ignore this step).  Instructions are available in my post and in another web site.

  5. Putting things together.  If you use MinGW and freeglut 32-bit, this step is quite simple, as Eclipse knows MinGW 32-bit settings well, provided that you save MinGW at C:\MinGW. If you choose a 64-bit path, follow a common instruction for building and running 64-bit applications with Eclipse + MinGW 64-bit in my post.
    1. Put freeglut library in a proper place.
    2. Set compiler and linker flags.  This can be done by going to 'Project->Properties'.  Then, in C/C++ Build, go to 'Settings'.  The flags for static and dynamic links are different.  I assume that you are going to build a C++ application.
      1. For static link:
        In 'GCC C++ Compiler', go to 'Preprocessor' and add a defined symbol FREEGLUT_STATIC.  Also, make sure that your compiler see your freeglut and OpenGL headers.

        In 'MinGW C++ Linker', go to 'Libraries' and add the following libraries (-l): winmm, gid32, opengl32, and freeglut64_static.  Note that the name for freeglut64_static must be consistent with your actual library file name.  Also, it is important to omit the prefix 'lib' and suffix '.a'.  It is crucial, however, that the library file name must come with the prefix and suffix.  Next, add library search path (-L).  This is where you have placed your freeglut library.  For example, in my case, I add a path "C:\MinGW_x64\x86_64-w64-mingw32\lib\freeglut" (with double quotes).

      2. For dynamic link:
        There is no need to add any thing to the preprocessor section, but make sure that your compiler see your freeglut and OpenGL headers.

        Now, go to 'MinGW C++ Linker' and its library section.  Then, do exactly the same thing as discussed for the static link, except that you have to change freeglut64_static to the one for your dynamic link.

    3. Build application.  Try starting with a simple one.  A good example is given from other www.transmissionzero.co.uk.

    4. Run your application.  This seems to be trivial at first, but it can be a bit tricky because you also have to set your MinGW library path.  In addition, if you dynamically link to freeglut, you need to set up its library path.  Whether or not you are building an 64-bit application, please see Section 'Change Run-Time Setting' in my previous post.  It shows you how to set run-time library paths in Eclipse and Windows command to set a library path in case that you are going to run your application outside Eclipse.  If you feel not understand how to do it, you might want to check some figures in my even older post (Figures 6 and 7).

Pinyo Taeprasartsit
(May 2010)

This document can be viewed at my blog and my Google docs.  I welcome questions, comments, and suggestions.  Please leave a message in my blog if you have any.  Thank you.



Build FreeGlut with MinGW

Build FreeGlut with MinGW

(updated Jan 21, 2011: new FreeGlut 64-bit pre-compiled binary is now available. This solves a naming issue caused by an earlier version of MinGW. Thank you James for pointing out the issue.)

FreeGlut 2.6.0 comes with Visual Studio 2008 solution files. If you use Visual Studio, you can probably build the package easily. If you, however, want to use MinGW to build FreeGlut (either or both 32-bit and 64-bit versions), you may not know where to start. Thus, I wrote this document, so that you can build FreeGlut yourself. If you prefer, there are several binary packages ready to use in some web sites. Example of these web sites are http://www.transmissionzero.co.uk/software/freeglut-devel/ and this web site itself.

32-Bit FreeGlut

Use MSYS to build 32-bit freeglut shared library [ref: http://netsuperbrain.com/blog/posts/freeglut-windows-hopengl-hglut/]
  1. gcc -O2 -c -DFREEGLUT_EXPORTS *.c -I../include
  2. gcc -shared -o freeglut32.dll *.o -Wl,--enable-stdcall-fixup,--out-implib,libfreeglut32.a -lopengl32 -lglu32 -lgdi32 -lwinmm

Use MSYS to build 32-bit freeglut static library
  1. gcc -O2 -c -DFREEGLUT_STATIC *.c -I../include (same as above, except the flag is changed from FREEGLUT_EXPORTS to FREEGLUT_STATIC)
  2. ar rcs libfreeglut32_static.a *.o [ref: http://www.adp-gmbh.ch/cpp/gcc/create_lib.html]

If you want to use a pre-compiled binary package, instead of building it yourself, get a pre-compiled binary package here.

64-Bit FreeGlut

This tutorial show a way to build 64-bit FreeGlut by using the Windows command prompt (cmd.exe). Almost every thing is the same as using MSYS for the 32-bit version. In this case, I assume that the current directory is the freeglut source directory and MinGW x64 is at C:\MinGW_x64\bin\.

First of all, set PATH to MinGW in the command prompt:
set PATH=%PATH%;C:\mingw_x64\bin

For a shared library (note that actual prefix gcc-related file names may be different from distribution to distribution)
  1. x86_64-w64-mingw32-gcc -O2 -c -DFREEGLUT_EXPORTS *.c -I../include
  2. x86_64-w64-mingw32-gcc -shared -o freeglut64.dll *.o -Wl,--enable-stdcall-fixup,--out-implib,libfreeglut64.a -lopengl32 -lglu32 -lgdi32 -lwinmm

For a static library
  1. x86_64-w64-mingw32-gcc -O2 -c -DFREEGLUT_STATIC *.c -I../include (same as above, except the flag is changed from FREEGLUT_EXPORTS to FREEGLUT_STATIC)
  2. x86_64-w64-mingw32-ar rcs libfreeglut64_static.a *.o

If you want to use a pre-compiled binary package, instead of building it yourself, get a pre-compiled binary package here. This package was built with sezero's build MinGW GCC 4.4.5 20101001.

Pinyo Taeprasartsit
(May 2010, Jan 2011)

This document can be viewed at my blog and my Google docs. Please leave a message in my blog if you have any questions, comments, and suggestions. Thank you.

Eclipse MinGW x64

Eclipse CDT with MinGW w64

This tutorial discusses how you can use MinGW 64-bit and Eclipse to build an application.  This tutorial is based on 64-bit MinGW (probably version 4.5--the version is not clear from the download page) and Eclipse Galieo (SR2 Build ID:2010 02 18-1602) and Eclipse Helios (SR1 for Windows 64 bit, eclipse-cpp-helios-SR1-win32-x86_64). The following parts of this tutorial assume that you already installed Eclipse and MinGW 64-bit at your preferred paths and will only discuss how you can change Eclipse settings to build and run your applications.

Get Compiler

The first task is to obtain a proper MinGW w64 compiler.  Although our target platform is Windows 64-bit, the compiler may be a 32-bit application that performs cross-compilation for you.  You can choose your preferred version from here.  There are some significant different for personal and automated builds (as of Dec 2010).  For example, the sezero build I used (mingw-w64-bin_x86_64-mingw_20101003_sezero) is 64-bit binary producing 64-bit application, while a binary from the automated build (mingw-w64-1.0-bin_i686-mingw_20101129) is 32-bit binary generating 64-bit application.  Technically, the automated build performs cross-compilation, while the sezero build performs native compilation.  As sezero build has a conventional directory structures and file names (same as MinGW 32-bit), I recommend using the sezero build.  It will save you a lot of time and you will not encounter some naming troubles in later steps.

In summary, from the link provided above, it is best if you go to the 'Personal Builds' section and download the latest sezero build.  This is probably the best way to use Windows 64-bit to generate 64-bit applications.

Change Compiler Settings (Galieo SR2)

  1. Go to menu Project->Properties.

  2. Go to node C/C++ Build->Environment.

  3. Update the path variable according to your MinGW w64 installation.  For example, change it to C:\MinGW_w64\bin. 

  4. Switch to topic C/C++ Build->Settings.

  5. [update (Dec 27, 2010): some recent MinGW versions (native compilation) come with 'as', 'g++', and 'gcc' commands. This step may be unnecessary for your MinGW build. However, some versions (cross-compilation) still have this issue and this step is required.  If you use sezero build, this step is probably unnecessary.]

    There are four commands to update: GCC Assembler, GCC C++ Assembler, GCC C Compiler, and MinGW C++ Linker.  Correct commands depends on the platform of MinGW w64 you choose.  No worry.  It is quite obvious to know what commands to use, as the command names are in the format [platform prefix]-[default command].exe.  For example, we need to update the command 'as', 'g++', 'gcc', and 'g++' for GCC assembler, C++ compiler, C compiler and C++ linker, respectively.  The correct commands may be 'x86_64-w64-mingw32-as', 'x86_64-w64-mingw32-g++', 'x86_64-w64-mingw32-gcc', and 'x86_64-w64-mingw32-g++', respectively.

Change Compiler Settings (Helios SR1)

Eclipse Helios is virtually blank regarding compiler configurations, but it seems to be compatible with various GCC toolchains (Mac, Solaris, Cygwin, MinGW, and Linux).  Our first task is to choose a proper toolchain as follows.
  1. Go to menu Project->Properties.
  2. Go to node 'Tool Chain Editor.' (The spelling of toolchain in Eclipse is not consistent.  There are both 'tool chain' and 'toolchain.')
  3. Set the current toolchain to 'MinGW GCC.'
  4. Set the current builder to 'CDT Internal Builder.'  You are free to use 'GNU Make Builder' if you prefer.
  5. Go to node 'Settings.'
  6. Change the path of compiler/assembler commands.  For example, highlight 'GCC C++ Compiler' and change the command to 'C:\MinGW_64bit\bin\g++' if you save MinGW at C:\MinGW_64bit.
     
There is no path variable any more, but there is a new node called 'Discovery Options' inside 'C/C++ Build.'  If we check 'Automate discovery paths and symbols,' most path settings are not necessary during program compilation.

If you employ OpenMP, you might want to see more detail at this site.  Basically, you have to change the miscellaneous flag of GCC C++ Compiler and MinGW C++ Linker to include -fopenmp.  Remember, however, that we have to change run-time settings, as OpenMP requires additional run-time libraries.

Change Default Include Folders

(This step is required for Galieo, but not for Helios.  If you use Helios and choose 'Automate discovery paths and symbols' in 'C/C++ Build,' the defualt include folders should be correct right of the beginning.)

It is quite painful in this part, as Eclipse uses the C:/mingw by default.  If you have multiple versions of MinGW, you have to change them.  Fortunately, (almost) all include files are all the same among versions.  Therefore, if we add header files to the default MinGW installation path, things should work across versions.  These are, however, the include paths for the Eclipse editor.  This means that Eclipse source-code editor can parse and show some hints if we add header files to the default 32-bit MinGW path, even though we are going to use 64-bit MinGW to perform actual program compilation.  Therefore, for actual compilation, we need true addendum to an include folder that a compiler uses.  See an example below.

Example: add OpenGL header files to Eclipse for actual compilation.
If we want to add header files to a 'global' include folder, such as GL, the include folder that will make things work has a platform prefix, say 'x86_64-w64-mingw32\include'.  This is the case when we call gcc with the same prefix from a bin directory, such as 'x86_64-w64-mingw32-gcc.exe'.  Addition of header files in other directories has no effect in actual compilation.

Change Run-Time Settings

At this step, you can build your 64-bit application, but you probably see that you cannot run it, even within the Eclipse environment itself.  Your program might just be terminated without any reported error message.  To deal with the problem, you also need to change run-time settings as follows.
  1. Go to menu 'Run->Run Configurations...'
  2. Go to tab 'Environment'.
  3. Create a environment variable by using the 'New' button.
  4. In a new popup window, set 'Name' to PATH and 'Value' to $PATH;C:\MinGW_64bit\bin, provided that C:\MinGW_64bit is the installation folder for your 64-bit MinGW.  Note: if you have more paths to include, separate them with a semi-colon.

Alternatively, you can set Windows environment path variable to include the MinGW path.  Nonetheless, if you have both 32-bit and 64-bit MinGW in the paths, some DLL names may cause DLL conflict and you may get 32-bit library instead of 64-bit library when you call your program.  If your machine, however, has only 64-bit MinGW, using the Windows path variable is probably the most convenient way because you will have no need to create a path variable in Eclipse run-time settings for this purpose again.  Nevertheless, dealing with Eclipse run-time settings does not need admin previlage to execute and it is always a viable option.

Because setting the Eclipse environment variable does not change any path variable outside Eclipse, we cannot run the application by double clicking on it or calling at command prompt, unless path variable for the executable session is set.  To temporarily change Windows path variable, a command 'set path=%path$;C:\MinGW_64bit\bin' may be used [ref:http://vlaurie.com/computers2/Articles/environment.htm].  Again, separate library paths with a semi-colon if you have multiple library paths to add.  This effectively appends the MinGW path to the existing path variable, specifically for the command prompt session.  Therefore, it may be a bit awkward if we want to call an executable file from Windows Explorer, as a batch file may need to be created.

OpenMP Note: for sezero's MinGW build, you may need to unpack pthreads-w64.zip into the root of your MinGW folder (C:\MinGW_64bit) so that required OpenMP run-time libraries are available in a proper place.

Tips: some error messages from Windows may not show up in Eclipse when we run or debug our programs within Eclipse environment.  Try using a command prompt (cmd.exe) and you may see more informative error messages.

Optional: Using MSYS to check if the compiled application is 64-bit

There are at least two ways to check if an application is 64-bit or 32-bit.  The easiest way is to run it and see its process name in the Windows Task Manager.  If the process name has a suffix *32, it is 32-bit.  If there is no such suffix, it is 64-bit.  There is another way.  It is less convenient in my opinion, but you can probably use it to check your application programmatically without running it at all.  This is possible if you use MSYS as discussed below.

  1. Copy the executable file to a place where MSYS environment can reach.  This is probably your home folder in C:\msys.

  2. Enter 'file my_executable.exe'.  For MSYS 1.0.11 32 bits (I ran it on 64-bit Windows 7), a 64-bit executable will get a message 'my_executable.exe: PE executable for MS Windows'.  There is nothing about 32-bit or 64-bit tags showing up.  A 32-bit executable, however, will get a message 'my_executable.exe: PE executable for MS Windows (console) Intel 80386 32-bit'.  Namely, a 32-bit executable gets a clear description about its platform, while a 64-bit executable does not get any definite description.  This is analogous to what the Task Manager does.

    For more recent version of MSYS, the file command gives PE32 for a 32-bit executable and PE32+ for a 64-bit executable.


Pinyo Taeprasartsit
(May, Dec 2010)

This document can be viewed at my blog and my Google docs.  I welcome questions, comments, and suggestions.  Please leave a message in my blog if you have any.  Thank you.






Saturday, February 06, 2010

Redistributable Documents

Redistributable Documents

This web page hosts redistributable documents.  The aim of this web page is to make things more convenient for those who want to access the document off-line or to obtain some software bundles in a convenient way.  Documents that are not redistributable are noted and links to the official web sites are provided, instead.

OpenGL 2.1 Reference Pages (Man Pages)

(Feb 2010)
These man pages are a slightly modified version provided by the Khronos group.  Typically, we have to SVN to get these man pages.  The official package of the man pages, however, contain more information than what I need (most people may feel the same).  If you want only HTML man pages, you might want to download my package (2MB).  There is only one addition, index_frame.html, which is a frame version of the man page we see in this official web site.  I hope this helps those who do not want to deal with SVN and want the frame version.

MinGW 32-bit

(May 17, 2010)
Since MinGW installer is not finalized yet, it is awkward to get GCC 4.5.0 from the server.  Therefore, I gathered all files that are downloaded by mingwdl.sh discussed in MinGW web site and made a package that is ready to use.  Just download the package if you don't need anything newer than the April-16-2010 release (GCC 4.5.0).  With the package, there is no need to bother with wget or bash.


Pinyo Taeprasartsit

(This document can be viewed at my blog and my Google docs)





Thursday, February 04, 2010

Dealing with Build Errors in Visual C++ Project

Dealing with Build Errors in Visual C++ Project

Warning C4251 for vector type in DLL build

When we build a shared library, it is possible that container specialization type is not available outside the scope of the shared library, as the caller may not know such type.
This, however, generates only warning for an STL container.  We can deal with this warning by explicit declaration of the library interface.

For example: template class DLL_IMEXPORT_DEF std::vector<int>;

Unfortunately, there may be another warning if the type in the STL container is a class or a pointer to an object, not a primitive type.
For example: template class DLL_IMEXPORT_DEF std::vector<XmlNode>;
causes a warning about std::allocator<_Ty,_Alloc>.

This problem can be solved (sort of, see the note below) by adding a dll interface of the problematic allocator in front of the export of the intended STL container.
That is, we have to write (order of declaration is important).
template class XML_MANAGER_IMEXPORT std::allocator<XmlNode>;
template class XML_MANAGER_IMEXPORT std::vector<XmlNode>;
to be free of the warning.

A vector of CString has some special requirements for its DLL interface.  I need to have additional interface declaration, as shown below.
template class XML_MANAGER_IMEXPORT std::allocator<CString>;
template class XML_MANAGER_IMEXPORT std::_Container_base_aux_alloc_real< std::allocator<CString> >;
template class XML_MANAGER_IMEXPORT std::vector<CString>;


Note: exporting STL template specialized class may cause link error with multiply defined class.  Microsoft discusses about a way to deal with STL export in this link

More info is available at:
http://www.unknownroad.com/rtfm/VisualStudio/warningC4251.html

Polymorphism with Boost shared_ptr

Using a shared_ptr is a great way to avoid almost all kinds of memory leak.  Moreover, we have no need to find out the last owner of an object.  Therefore, complexity in programming logic is reduced.  Unfortunately, it is not straightforward to employ polymorphism with a shared_ptr, as we cannot just cast the pointer in a typical C++ fashion, e.g. dynamic_cast.  Thus, Boost library comes with a special way to cast its smart pointers.

boost::shared_ptr<T_A> testPtr(new T_A(blah));

boost::shared_ptr<T_B> anotherTestPtr = boost::dynamic_pointer_cast<
T_B>(testPtr));

That is 'boost::dynamic_pointer_cast' is a way to solve this issue.

More info can be found here and there.

====================

If you deal with the polymorphism in a vector.  For example, we want to have std::vector< boost::shared_ptr<T_Base> > to hold shared_ptr of T_Child1 and T_Child2.  In this example, we can do it as follows.

std::vector< boost::shared_ptr<T_Base> > vecPoly;
boost::shared_ptr<T_Base> childPtr1( new T_Child1() );
boost::shared_ptr<T_Base> childPtr2( new T_Child2() );

vecPoly.push_back( childPtr1 );
vecPoly.push_back( childPtr2 );

There is nothing to worry about calling virtual functions.  They will be called properly as usual.

Definition of a template class in a DLL cannot be found

If a template class is not employed within the DLL itself, its actual binary may not be created at a compile time or link time.
Also, if we inline everything in the template class and use the class inside the DLL, its actual binary may not be created and accessible by other DLLs or applications.
We can deal with the problem by specialization of the template class and avoiding inlining constructors and destructors of the class.  Performances may decrease, but it solves the problem.

Error with afximpl.h

When a project incude afximpl.h (typically in stdafx.h), compile errors may show up in Visual Studio 2008 (Visual C++ 9.0).  The error message looks like:
1>c:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\include\..\atlmfc\src\mfc\afximpl.h(635) : error C2059: syntax error : '<L_TYPE_raw>'
1>c:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\include\..\atlmfc\src\mfc\afximpl.h(635) : error C2238: unexpected token(s) preceding ';'

It seems that Microsoft did not purge this error yet, but there is a workaround that is effective.

"place the following line in your code before including afximpl.h:
DECLARE_HANDLE(HRAWINPUT); "

Reference: http://connect.microsoft.com/VisualStudio/feedback/details/276404/windows-2000-settings-incompatible-with-afximpl-h#details

error LNK2005: "void * __cdecl operator new(unsigned int)" (??2@YAPAXI@Z) already defined in ...

Cause:
"The CRT libraries use weak external linkage for the new, delete, and DllMain functions. The MFC libraries also contain new, delete, and DllMain functions. These functions require the MFC libraries to be linked before the CRT library is linked." [Reference]

This can be fixed by forcing the order of library linking.  This is shown in the above reference and directly quoted here:

"Visual C++ does not contain this header file. To create this file, follow these steps:
  1. Open Msdev\Mfc\Include\Afx.h.
  2. Select the lines between #ifndef _AFX_NOFORCE_LIBS and #endif //!_AFX_NOFORCE_LIBS.
  3. Copy the selection to the Windows Clipboard.
  4. Create a new text file.
  5. Paste the contents of the Clipboard into this new file.
  6. Save the file as Msdev\Mfc\Include\Forcelib.h."

Separate inline function declaration and definition

In Visucal C++, separating an inline function declaration and definition across header and source files is not possible, unless the inline function is virtual.  However, we can separate declaration and definition in the same header file.  The only difference is that we should add a keyword inline in front of the definition.  Example:

"When you declare an inline member function, it looks just like a normal member function:

 class Fred {
 public:
   void f(int i, char c);
 };

But when you define an inline member function, you prepend the member function's definition with the keyword inline, and you put the definition into a header file:

 inline
 void Fred::f(int i, char c)
 {
   ...
 }

It's usually imperative that the function's definition (the part between the {...}) be placed in a header file. If you put the inline function's definition into a .cpp file, and if it is called from some other .cpp file, you'll get an "unresolved external" error from the linker."  [This is quoted from another web site.]




------------------------------------
Pinyo Taeprasartsit

(This document can be viewed at my blog and Google docs)


Building and Debugging Tips in Visual C++

Building and Debugging Tips in Visual C++

(Jan 2010-Present)
This document compile my ongoing experience with Visual C++.  There are many more tips I know, but they are related to generic C++.  I'm thinking about creating a separate document for them or just put them here.

Can we debug a DLL project?  Why can we set a DLL project as a startup project, even though it is not executable per se?

Yes, we can debug a DLL project.  Plus, it does make sense to make it a startup project.  In fact, setting it as a startup project is a way to debug the DLL project, provided that we have any executable file to call the DLL.

Complete info is a available in this link.  Here, I quoted the essential information for Visual C++.

"

To specify the calling application in a C++ project

  1. In Solution Explorer, select the DLL project.

  2. On the View menu, choose Property Pages.

  3. In the Project Property Pages window, in the Configuration drop-down list, choose Debug.

  4. Open the Configuration Properties folder, and select the Debugging category.

  5. In the Debugger to launch list, choose Local Windows Debugger or Remote Windows Debugger.

  6. In the Command or Remote Command box, click the drop-down arrow, and select Browse from the list to locate the application. Alternatively, type the path and name of the application.

  7. Type any necessary program arguments in the Command Arguments box.

"

What if a break point cannot be hit because 'no symbol has been loaded for this document'.

In Visual C++, this seems to be a common problem.  First of all, we have to check if we have debugging symbols loaded or not.  Go to the 'Output' window (this is typically one of the tabs at the bottom of your VC++ window).   Then, look for a line telling about loading DLL.  For example,  there should be a line like this

'TesterConsole.exe': Loaded 'F:\Work\A.dll', [IMPORTANT MESSAGE HERE]

Next, check what your IMPORTANT MESSAGE HERE is.   If it is 'Binary was not built with debug information', we have to check a few points in our project properties.  Go to the project properties.  In the tab 'Configuration Properties -> C/C++ -> General', it is best that 'Debug Information Format' is a good one, which is 'Program Database (/Zi)' and 'Program Database for Edit & Continue (/ZI)'.  Then, go to a neigboring tab 'Optimization', it is highly recommended to set 'Optimization' to 'Disabled (/Od)'.

The final step is check your linker option.  Go to the tab 'Configuration Properties -> Linker -> Debugging'.  Next, set 'Generate Debug Info' to 'Yes (/DEBUG)'.
(Some additional information is in this link.)

If your IMPORTANT MESSAGE HERE is NOT about 'Binary was not built with debug information', chances are your debugging information is not synchronized with your DLL or your PDB (program data base) file is corrupt.  Try cleaning and rebuilding the affected DLL project.

Stand Between Debug and Release Modes

In some cases, it is too time consuming to debug a program in the Debug mode for a computationally intensive application, especially if we only want to see a crash site and call stack.  In such cases, we can choose to create another configuration.  Let's call the configuration 'Release w Assertion Enabled'.  This is actually a release mode without a preprocessor symbol NDEBUG.  Without NDEBUG, all assertion mechanisms are performed like in a debug mode, but the code is optimized.  If your program crashes, you can see the crash site and call stack, even though you cannot see accurate values of variables.  This is significantly useful when we need to get some idea about the problem. 

At this point, you might ask, 'Why do we need another configuration?' and 'Why don't we just remove NDEBUG during debugging process and insert it back when debugging is done?'  The answer is if we change any preprocessor symbol, the compiler must re-compile everything.  This is very time consuming if your project is large.  Moreover, in most cases, the debugging process never ends.  We have to go back and forth between release and debug builds all the time.  Creating a separate build, therefore, is a better way for a large and computationally intensive project in the long run.

Playing around with Visual C++, I found that Visual C++ might behave strangely if I modify a debug mode by just removing the preprocessor symbol _DEBUG and letting the executable call another library built in a release mode.  In the scenario, the run-time check might find that the heap or stack around a variable from the library is corrupted, but there is actually nothing wrong.

String Conversion

String conversion is usually necessary when we work with XML data, as XML uses Unicode character set by default.  Most parts of our work, however, may mainly use the ANSI character set.  This typically requires us to employ CA2W to convert an ANSI text to a Unicode text.  The usage of CA2W, however, may be quite unnatural to most of us.

For example, the use LPCWSTR szr = CA2W(szReplaceFile); is actually invalid because LPCTSTR becomes a pointer to a temporary string created during a conversion.  Therefore, it will be scrapped immediately after CA2W returns.  The correct use is CA2W szr(szReplaceFile);.  Look strange, right?  No doubt, a macro is usually strange.

Assertion Failed at the line ASSERT(AfxGetThread() == NULL)

This assertion is at the line 380 in appcore.cpp of Visual C++ 2008--version 9.0.  (Note that other versions may have this assertion at different lines.)  This issue arises if there are multiple MFC-application threads running at the same time (to be more specific, they are generated by CWinApp objects).  It is usually caused by calling MFC DLLs that comes with their own 'theApp', an object derived from CWinApp.  When these DLLs are initialized, they may start their own MFC-application threads, while we expect to have only one MFC-application thread--the one from our executable project. 

There is a discussion about this issue in this link.  Unfortunately, it does not tell us much about any practical approach for a solution. Thus, we have to figure this out ourselves.  If your application involves many DLLs, you probably have no idea what DLLs are culprits.  After trying for hours, I finally come up with a systematic method to identify the problematic DLLs and, of course, I have a solution for this problem.

To begin, set a break point at the assertion line ASSERT(AfxGetThread() == NULL).  Then, start the debugger.  At the break point, check 'Call Stack'.  (Those who are not familiar with debugging and the call stack might want to see this reference.)  If the call stack involves a DLL that is not mfcXXd.dll, such a DLL is likely to be a culprit, especially if it uses MFC in a shared library.  

Next, check the source code of the library and look for the file with 'theApp' in the suspect library.  Then, check its InitInstance() function.  If you find no AFX_MANAGE_STATE(AfxGetStaticModuleState( )); at the beginning of InitInstance(), insert it at the very beginning of the function.  Then, continue this process until every involved CWinApp DLL is properly equipped with AFX_MANAGE_STATE(AfxGetStaticModuleState( )).  

If the AFX_MANAGE_STATE causes linker errors, you might have an invalid preprocessor symbol.  The CWinApp DLL is usually configured with _USRDLL, not _LIB, because it is a dynamically linked library.  You may find that changing _LIB to _USRDLL helps solve your linker errors.

Halt at First-Chance Exceptions

First-chance exceptions in Visual C++ are a kind of access violation with code c0000005.  Many of them are not critical and we can possibly ignore them.  However, it is better, if we can get rid all of them, as your customers may demand that no compiler/linker warnings and no such exceptions are present when you ship your product.  

Visual C++, nonetheless, may not stop at the problematic point and just continue.  This makes it hard to tackle the problem.  So, we might want to force break for all exception.  The exception sheet can be accessed from menu Debug -> Exceptions (also by Ctrl-Alt-E, see Figure FCE1) in Visual Studio 2008.

If all exceptions are to be tackled, check all of the check boxes there so that all exception will be thrown (and we are forced to handle or get rid of them from the first place).  See Figure FCE2.  If only first chance exceptions, which are in fact a kind of access violtion, are to be handled, go to Win32 Exceptions and check Access Violation (Figure FCE3). 

Note that there is a button on the right that can reset exception settings to default and can even add a new one to the list (Figure FCE 4).  Adding a new one can possibly add our custom exceptions to the list, I belive (not try it myself yet).

Figure FCE1.  Exeption setting menu


Figure FCE2.  Set to throw all exceptions known by Visual C++.

Figure FCE3. Focus on first-chance exception.  However, it is recommended to (finally) handle all kinds of exceptions that come into your way.

Figure FCE4.  Remember that we can reset the settings or add a new exception to the list.

Acknowledgement: Peter Kellner

Program Exits with Code 0, but Unexpectedly

Typically, a program exiting with code 0 means there is nothing wrong.  However, sometimes a program exits in this way unexpectedly.  Also, nothing shows up at all.  I encountered this problem when I tried to build a program that uses a statically linked library.  That library, nonetheless, was created as a dynamically linked library before.  Therefore, there is a variable theApp inside its main file.

In general, this does not cause any problem and we can switch between dynamic-lib and static-lib configurations any time we want.  Nevertheless, there is one thing we need to be careful.  In a static-lib configuration, we are not supposed to let the program execution see theApp.  Why?  Because theApp will be initialized and start an instance of an MFC application thread.  Subsequently, theApp just exits with code 0.  This problem arises if we add something to the library main file and that file is in a part of compilation/execution.

To check if that is the actual cause of the problem, set a break point at the beginning of InitInstance() in a suspect library.  If the function is called, theApp is initialized in a wrong way.  Therefore, we need to find a way to prevent theApp to get involved.  This can be done by removing things that you add to the library main file.  If that means a lot of work, try using the macro to exclude theApp. 

For example:
#ifdef _NO_THEAPP
CLibApp theApp;
#endif

The above lines of code should effectively deal with the problem, provided that _NO_THEAPP is defined properly (as a preprocessor, for example).


Pinyo Taeprasartsit

(This document is viewable at pinyotae.blogspot.com and docs.google.com)