Table of Contents
This article discuss implementation details of PicZoom
application [A photo viewer with some functionalities are similar to the Picasa Photo Viewer]. PicZoom
is an MFC Dialog based application, and OpenGL is used for drawing. Shader programs are not used for display, and therefore I hope PicZoom
will run without a high-end graphics card. Since it requires some graphics memory to prepare all textures [Background, Image, Buttons, etc], graphics card is required to run PicZoom
. Limitations section explains graphics memory limitation of PicZoom.
When I started studying OpenGL, I created a simple application to load an image from file and do simple rotation, zoom with it. OpenGL provides APIs to do some simple image operations such as zoom, pan, rotate. Picasa Photo Viewer is a simple and superb image browser tool. In this article, I am just imitating some functionalities of Picasa Photo Viewer with OpenGL and MFC. I’m sure performance of this application is not satisfying with Picasa, but it’s an attempt to do something with OpenGL and MFC. Any comments and improvement suggestions are welcome.
Screenshot of PicZoom [With some image operations].
Screenshot of PicZoom in dialog mode.
Initially, I did almost all functionalities of PicZoom
in a single class, CPicZoomDlg
. It was very difficult to manage everything in a single class. Therefore, I prepared different classes based on different functionalities. For example, ImageArea
class will handle all operations [draw, zoom, pan, mouse cursor based on image] related to Image. Then PicZoomDlg
will create objects of ImageArea
, Background
, etc., and manage them with less effort. And I prepared opengl wrapper classes, to handle initialization, texture creation, text drawing, circle drawing, etc.
When starting PicZoom
, it captures the desktop background and creates a semitransparent view to the background. The implementation details are as follows:
The screenshot of PicZoom with semi-transparent desktop as background.
Just call ::GetDesktopWindow()
and retrieve desktop window handle. Then prepare a memory dc and read contents [RGB data] of desktop window. GetDIBits()
provides RGB buffer of desktop window to an allocated memory. Here is the code to capture screenshot of desktop.
bool ScreenCapture::TakeScreenShot()
{
HWND hDesktop = ::GetDesktopWindow();
CDC dc;
HDC hdc = ::GetWindowDC(hDesktop);
dc.Attach(hdc);
CDC MemoryDC;
MemoryDC.CreateCompatibleDC(&dc);
CBitmap BmpObj;
BmpObj.CreateCompatibleBitmap(&dc, sz.cx, sz.cy);
CBitmap * oldbm = MemoryDC.SelectObject(&BmpObj);
MemoryDC.BitBlt(0, 0, sz.cx, sz.cy, &dc, 0, 0, SRCCOPY);
int nStatus = ::GetDIBits( MemoryDC.m_hDC, (HBITMAP)BmpObj.m_hObject, 0, sz.cy,
m_pBuffer, &stBitmapInfo,DIB_RGB_COLORS );
}
Main Components
The above figure shows the split up of main classes used in PicZoom
. PicZoom
creates a MFC
dialog, and the painting of client area is handled with OpenGL
.
Initially, PicZoomDlg
creates 4 main components of PicZoom
, [Background
, ImageArea
, BottomWindows
, and CloseButton
] and adds to a vector named WindowList
. Whenever we get WM_PAINT
in dialog, Draw()
commands are passed to all the 4 components. Like
<code>Draw
(), all mouse messages are also passed to the components.
All these classes [Background
, ImageArea
, BottomWindows
, and CloseButton
] are derived from GLWindowBase
, the base class designed for handling different windows( or image components).
This class is responsible to create one texture with desktop image. The RGB buffer with screenshot of desktop is obtained with ScreenCapture
class. The background image texture is created with 25% transparency. It is achieved with glPixelTransferf()
before texture loading. glPixelTransferf
is used to provide scale factor of Red, Green Blue channels.
glPixelTransferf( GL_RED_SCALE, 0.75 );
glPixelTransferf( GL_GREEN_SCALE, 0.75 );
glPixelTransferf( GL_BLUE_SCALE, 0.75 );
if( !m_pImage->Create( m_nWidth, m_nHeight, pbyScreenCapuredData ))
{
AfxMessageBox(L"Texture Creation failed.");
return false;
}
glPixelTransferf( GL_RED_SCALE, 1.0 );
glPixelTransferf( GL_GREEN_SCALE, 1.0 );
glPixelTransferf( GL_BLUE_SCALE, 1.0 );
The
Draw()
handler just displaying this texture to the e
ntire screen. When switching from Desktop mode to Dialog mode, the
Background
instance is deleted from the
WindowList
and the background drawing is avoided.
bool BackGround::Draw()
{
if( !GLWindowBase::m_bVisible )
{
return true;
}
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
m_pImage->Enable();
m_pVertexBuffer->DrawVertexBuffer( GL_POLYGON );
return true;
}
The display and all operations (Zoom
, Translation
, Rotation
) of currently loaded image file is handled in this class.
ImageArea
creates one opengl texture with RGB data of the image file. Whenever user requests a new image file, ImageArea
creates the RGB buffer of new file with BMPLoader
class. BMPLoader
class uses GdiPlus
for creating RGB buffer of image file of type *.jpg, *.bmp, *.png, and *.tga. Whenever Draw
() receives, ImageArea
will display this texture with zoom, translation, and rotation. The details of Zoom, Translation and Rotation are explained below.
Zoom functionality is simply achieved with OpenGL scale feature. glScalef()
is called before displaying the texture, and therefore the texture will be scaled based on the current zoom factor.
Whenever mouse wheel received in Dialog, Dialog will send it to all components, and therefore ImageArea
will receive mouse wheel message. The zoom factor is calculated based on the current scroll value.
bool ImageArea::OnMouseWheel( const int nX_I, const int nY_i,
const UINT nFlags, const short zDelta )
{
float fZoomValue = float( zDelta ) / WHEEL_DELTA;
if( 0 == m_fZoomOffset )
{
::SetTimer( m_hParentWindow, TIMER_ZOOM, 5, 0 );
}
if( m_nImageWidth > m_nWidth || m_nImageHeight > m_nHeight )
{
float fImageToDesktopRatioX = (float)m_nWidth / m_nImageWidth;
float fImageToDesktopRatioY = (float)m_nHeight / m_nImageHeight;
float fImageToDesktopRatio =
min( fImageToDesktopRatioY, fImageToDesktopRatioX );
fZoomValue = fZoomValue * fImageToDesktopRatio;
}
m_fZoomOffset += ( fZoomValue / 100 );
m_ZoomTimer.SetMaxElapseTime( 15 );
return true;
}
Inside ImageArea::Draw()
, Zoom factor is added to current zoom factor, and then calls glScalef( m_fZoom, m_fZoom, 1.0 )
, scaling is not applied in Z order, since it is not required.
Whenever Zoom Factor is changing, ZoomText
class will display the new Zoom value. ZoomText
simply draws a rounded rect and displays the current zoom factor with text drawing of OpenGL
. FontEngine
class is created to handle the drawing of text.
FontEngine
creates a display list with bitmap of all characters. This can be simply achieved through wglUseFontBitmaps()
function of OpenGL
.
bool FontEngine::Create( HDC hDeviceContext_i )
{
VERIFY(m_pFont->CreateFont(
15, 7, 0, 0, FW_BOLD, FALSE, FALSE, 0, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY, DEFAULT_PITCH, L"Arial"));
HGDIOBJ hOldFont = ::SelectObject(hDeviceContext_i, m_pFont->m_hObject);
if( !wglUseFontBitmaps( hDeviceContext_i, 0, 256 * 2, 1000 ))
{
return false;
}
::SelectObject( hDeviceContext_i, hOldFont );
return true;
}
Screenshot of Zoom Text display.
Zoom Text displayed at center of dialog, with current zoom factor. This text is displayed in a semi transparent way. Hide and Show are very smooth, which is implemented by blending feature of OpenGL
. When drawing new object, new object and old object[already drawn object] are combined and create a transparent look of new object. Alpha value is started from 0.0, and increased 0.1 during each frame, and get a smooth appearance. When hidden, Alpha value is decreased from 1.0 to 0.0, and gets a smooth disappearance effect.
LButtonDown
and MouseMove
messages are tracked in ImageArea
, and find out the translation required in X and Y direction.When drawing the image texture, ImageArea::Draw()
will apply translation with OpenGL
translate function glTranslatef
.
glTranslatef( m_fXTranslation, -m_fYTranslation, 0.0 );
Rotation is simply implemented with glRotate()
function. The rotation angle is calculated based on the current rotation state. Possible rotation values are listed below:
const float ROTATION_ANGLES[4] =
{ 0.0, 270.0, 180.0, 90.0 };
Inside ImageArea::Draw()
, rotation angle is applied to z-order to achieve the required rotation.
bool ImageArea::Draw()
{
glRotatef( ROTATION_ANGLES[m_nRotate], 0, 0, 1 );
m_pVertexBuffer->DrawVertexBuffer( GL_QUADS );
return true;
}
Here we can discuss the vertex buffer logic. The below picture shows the 4 corners of image and its corresponding vertex coordinate. Rotation is applied to this vertex buffer and creates a rotated image display.
Image is displayed to screen using GLVertexBuffer
class. GLVertexBuffer
is a wrapper class for drawing any vertex with texture coordinate. glInterleavedArrays()
is called to draw one object to screen. In ImageArea
class, GLVertexBuffer
object is created with 4 vertices to draw the image into screen.
bool ImageArea::SetupWindow()
{
int nHalfOfImageY = m_nImageHeight / 2;
int nHalfOfImageX = m_nImageWidth / 2;
m_pVertexBuffer->SetAt( 0, -nHalfOfImageX ,
nHalfOfImageY, 0.0f, 0.0f,1.0f); m_pVertexBuffer->SetAt( 1, -nHalfOfImageX ,
-nHalfOfImageY, 0.0f, 0.0f,0.0f), m_pVertexBuffer->SetAt( 2, nHalfOfImageX,
-nHalfOfImageY, 0.0f, 1.0f,0.0f); m_pVertexBuffer->SetAt( 3, nHalfOfImageX,
nHalfOfImageY, 0.0f, 1.0f,1.0f); }
This class is responsible for drawing and message handling of buttons displayed at the bottom of PicZoom.
There are 9 buttons displayed at bottom of PicZoom, which will help to explore different image files in the current folder, zoom, and rotation of image.
Screenshot of BottomWindows.
GLButton
is designed to handle all operations related to one button. Bottomwindows
creates 9 instances of GLButton
, and hold in a list, and all commands are passed to the button list.
class GLButton : public GLWindowBase
{
public:
GLButton( HWND hParentWindow_i );
virtual ~GLButton();
virtual void SetRegion( const int nLeft_i, const int nTop_i,
const int nWidth_i, const int nHeight_i );
virtual void SetImage( const int nResourceID_i );
virtual bool SetupButton();
virtual void SetLButtonMessage( const int nMessageToParent_i );
virtual bool OnMouseMove( const int nX_i, const int nY_i, const int nFlags_i );
virtual bool OnLButtonDown
( const int nX_i, const int nY_i, const int nFlags_i );
virtual bool OnLButtonUp
( const int nX_i, const int nY_i, const int nFlags_i );
virtual bool IsWithinRegion( const int nX_i, const int nY_i );
void SetTransparency( const float fTransparency_i );
virtual bool Draw();
};
The initialization of a GLButton
is very simple. Three items are required to initialize a GLButton
. The resource ID, region of display and the Message ID. The resource ID of bitmap is used to create button image. The alpha channels of each pixel determine the transparency of button. When alpha values of a pixel is 0.0, then that pixel will not be displayed in button image. When user clicks the button, the message will send to the parent window(PicZoomDlg
).
The transparent behaviour of each button is achieved by the alpha blending technique. The bitmaps are created in RGBA format (one 8 bit channel for alpha component). GLTexture
class is modified to create RGB8, RGBA8, textures. The bitmaps required for bottom windows are added as resource of PicZoom, and that bitmaps are loaded with BMPLoader
, and create GLTexture
with RGBA data.
bool GLButton::SetupButton()
{
m_pVertexBuffer = new GLVertexBuffer;
if( 0 == m_pVertexBuffer )
{
return false;
}
m_pVertexBuffer->CreateQuadVertexBuffer();
UpdateVertexBuffer();
m_pTexture = new GLTexture;
int nWidth = 0;
int nHeight = 0;
BYTE* pbyARGBImage = 0;
BMPLoader LoadImage;
LoadImage.LoadBMP( m_nResourceID, nWidth, nHeight, pbyARGBImage, true );
m_pTexture->Create( nWidth, nHeight, pbyARGBImage, GL_RGBA, GL_RGBA8 );
return (GL_NO_ERROR == glGetError());
}
The mouse over effect of buttons is implemented in a tricky way. When displaying, the blending feature of texel (texture color) with current colour (glColor
) is enabled. Then set low color(glColor4f
(0.75f, 0.75f, 0.75f, 0.75f)) while displaying button in normal scenario. While displaying button with mouse over high color is applied glColor4f
(1.0, 1.0, 1.0, 1.0).
bool GLButton::Draw()
{
if( m_bMouseOver )
{
glColor4f( m_fTransparency, m_fTransparency,
m_fTransparency, m_fTransparency );
}
else
{
glColor4f( 0.75 * m_fTransparency, 0.75 *
m_fTransparency, 0.75 * m_fTransparency, 0.75 * m_fTransparency );
}
m_pVertexBuffer->DrawVertexBuffer( GL_QUADS );
return true;
}
The above screenshot displays the behavior of description text display. GLText
is created for displaying a text with smooth show and hide. Here also, alpha blending is used to make smooth appearance and smooth hide.
void GLText::Draw(const int nX_i, const int nY_i)
{
if( m_StringTimerHide.IsEnabled())
{
int nRemTime = m_StringTimerHide.GetRemainingTime();
float fColorComponent = ( nRemTime / 20.0 );
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glColor4f( 1.0, 1.0, 1.0, fColorComponent );
m_pFontEngine->DrawText( nX_i, nY_i, m_csDisplayString.GetBuffer( 0 ) );
glDisable( GL_BLEND );
glColor4f( 1.0, 1.0, 1.0, 1.0 );
m_StringTimerHide.ElapseTime();
}
else
{
m_csDisplayString = m_csDisplayStringNew;
int nRemTime = 20 - m_StringTimerShow.GetRemainingTime();
float fColorComponent = ( nRemTime / 20.0 );
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glColor4f( 1.0, 1.0, 1.0, fColorComponent );
m_pFontEngine->DrawText( nX_i, nY_i, m_csDisplayString.GetBuffer( 0 ) );
glDisable( GL_BLEND );
glColor4f( 1.0, 1.0, 1.0, 1.0 );
m_StringTimerShow.ElapseTime();
}
}
CloseButton
is derived from GLButton
, in order to implement some additional functionalities in CloseButton
. The circular shape of close button is implemented by setting alpha channels to outer region of circle to 0.0 and 1.0 to the inner region of circle. The mouse cursor changing is also based on the circle region.
bool CloseButton::IsWithinRegion( const int nX_i, const int nY_i )
{
if( GLButton::IsWithinRegion( nX_i, nY_i ))
{
int nXDiff = nX_i - GLWindowBase::m_nWidth;
int nYDiff = nY_i;
int nRadius = sqrt( (float)nXDiff * nXDiff + (float)nYDiff * nYDiff );
if( nRadius < 45 )
{
return true;
}
}
return false;
}
One workaround is also included in CloseButton
to create smooth edges of semi circle. When semicircle is texture mapped, the edges will not be smooth. Therefore one GLCircle
will draw a circle with 50% transparency. Therefore edges of close button will be smooth.
bool CloseButton::Draw()
{
GLButton::Draw();
m_CloseBoundary.Draw();
return true;
}
Slideshow is also implemented with alpha blending functionality. Two textures are created with bitmap data of two image files. Transition from first image to second is created by blending first and texture texels. When starting slide show, the window size is changed to full desktop size, then hides all other windows (BottomWindows
, CloseButton
, etc).
void SlideShow::Display()
{
glColor4f( fColorFactor, fColorFactor, fColorFactor, fColorFactor );
m_pTexture1->Enable();
glScalef( m_fZoom1, m_fZoom1, 0.0 );
m_pVertexBuffer1->DrawVertexBuffer( GL_QUADS );
glPopMatrix();
if( nRemTime < 100 )
{
glPushMatrix();
float fTex2Color = 1.0 - fColorFactor;
glColor4f( fTex2Color, fTex2Color, fTex2Color, fTex2Color );
m_pTexture2->Enable();
glScalef( m_fZoom2, m_fZoom2, 0.0 );
m_pVertexBuffer2->DrawVertexBuffer( GL_QUADS );
glPopMatrix();
}
}
PicZoom supports dragging of image files[*.bmp, *.jpg, *.gif, *.tga]. Drag and Drop is implemented with the help of article from jibesh[http://www.codeproject.com/KB/dialog/JibDragDrop.aspx].
WM_DROPFILES
message is handled in CPicZoomDlg
class, and file name can be retrieved by DragQueryFile()
function. Here is the code to handle new image file loading through drag and drop operation. LoadImage
creates a new ImageArea
and provides the new file name to this class. ImageArea
will display new image file in required zoom and pan.
LRESULT CPicZoomDlg::OnDropFiles(WPARAM wParam,LPARAM lParam)
{
TCHAR szDroppedFile[1024];
memset( szDroppedFile, 0, sizeof( szDroppedFile ));
HDROP hDrop ;
int nFiles;
hDrop = (HDROP)wParam;
nFiles = DragQueryFile(hDrop, 0, szDroppedFile, 1023 *2 ); LoadImage( szDroppedFile );
return 1;
}
PicZoom
: Application class of PicZoom
.When a new instance of PicZoom
starts, it checks already PicZoom
running. If one instance is running, then send a message to the old one, and exit. PicZoomDlg
: CPicZoomDlg
Dialog class. This class initializes OpenGL and, creates main components of PicZoom
. All messages received in this class are routed to the GLWindowBase*
objects in m_Windows
list. BackGround
: This class handles drawing of background image displayed in PicZom
. When full screen is on, the texture for the desktop image is created in this class. During each draw, this texture is drawn to the screen. BottomWindows
: This class holds all windows in the bottom area of PicZoom
. All GLButton
objects are created in this class, and messages are routed through this class. PicZoomDlg
will create BottomWindows
, and then send all messages to this class. ImageArea
: This class is responsible for drawing of a Image
. Whenever a new image file is loaded, ImageArea
will handle all operations related to one image. Rotate
, Zoom
, Translation
, are handled in this class. All mouse messages are received from PicZoomDlg
to this class, and handle necessary messages. Required timers are started for Zoom
, Translate.ImageArea
uses UnProject
class is used to identify the mouse position is within the image area, and ZoomText
class is used to draw current zoom value. CloseButton
: This class is responsible for drawing and message handling of close button. Mouse messages are routed to this class. When Mouse is clicked in this region, this class sends WM_QUIT
message to PicZoomDlg
. SlideShow
: This class is responsible for displaying the Slideshow of a folder. This class creates two textures, and makes the combined image with alpha blending. ImageFileFinder
class is used to find the image files. PlayButton
: This class is derived class of GLButton PlayButton
is circular shaped button, therefore mouse message handling is specially handled in this class. IsWithinRegion()
is overridden in this class to create a button mouse over effect for circular region of PlayButton
. For smooth edge, the outer region of play button is drawn with 2 GLCircle
objects. CoordConverter
: This class holds current window region, and it converts the window coordinate to opengl coordinate. This can be used for vertex buffer creation with window coordinates. NewFilLoader
: This class handles loading of new image. When user clicks context menu, then new (JPG, or BMP) file should be loaded in the old process. When an instance of PicZoom
is running, the new instance will set an event and write the name of file in a shared memory, and that memory will be used to get the name of new file name. UnProject
: This class is used to get opengl vertex coordinate from screen coordinate, and then identify whether the specified position is within the Image
area. ZoomText
: This class displays current Zoom
factor in center of PicZoom
Dialog. Smooth show and hide is implemented with alpha blending. CursorManager
: This class creates different cursors, and any other class can use this class for changing the cursor. FileExplorer
: This class is responsible for handling the next file and previous file providing to the Dialog
class. Whenever user press Next
, Previous
, Dialog
class calls GetNextFileName
to retrieve the name of Next/Previous file. On changing the Folder, SetFilePath
of this class is called by Dialog
. For performance reasons, this class creates a vector and holds all image files in that vector. This vector is created by a new thread. GLWindowBase
: Base class of all opengl windows created in PicZoom
.
BMPLoader
: This class can load a bitmap from a file or Resource. LoadBMP
returns the allocated buffer, with width and height. GdiPlus
functions are used to load different image file formats. Timer
: This class handles Timer
functionality. The maximum time is set by SetMaxElapseTime()
, and this ElapseTime()
reduces time. ScreenCapture
: This class takes screen shot of Desktop window. GetBuffer
provides RGB data of Desktop window. PicZoomUtil
: This class provides static
functions for some functionalities required in PicZoom
. GetNearestPowerOf2
provides nearest power of 2 of an integer. This function is used for texture creation when non_power_of_two opengl
extension is not available. ImageFileFind
: A CFileFind
derived class which will find image files only. CursorManager
: This class creates different cursors, and any other class can use this class for changing the cursor.
FontEngine
: This class handles rendering of all texts in PicZoom
. Creating a font display list, and drawing all text. All text drawing is handled with this class. This class holds width and height of all characters to draw string
in correct alignment and position. GLButton
: This class handles all operations related to a Button
.The drawing and mouse message handling is handled in this class. The resource ID of bitmap is provided to this class, and ID of Message to send to parentWindow( PicZoomDlg)
is also provide to this class. Whenever user presses the button, this class will send message to PicZoomDlg
.GLCirle
: This class is used to draw a circle, or semi circle. The start and end angles are provided to this class. The draw action creates a blended circle, and this class is used for creating smooth edges for Play button, and Close button. GLExtension
: This class is used to determine GL_ARB_texture_non_power_of_two
extension exist or not. m_bNonPowerOfTwo
flag is set or reset based on the availability of GL_ARB_texture_non_power_of_two
extension.GLSetup
: This class setup opengl. Creates opengl rendering context and makes it current. GLText
: This class simply handles drawing of Text. Show and hide are very smooth with this class.GLTexture
: Handles texture related operations in this class.GLVertexBuffer
: This class create a set of vertices of GL_T2F_V3F
type. SetAt
will update the vertex information of a index. DrawVertexBuffer
will pass the vertex information to GPU.
PicZoomInstaller
is created to modify the registry entries, which are required to add context menu in windows explorer. When right click a file, the following context menu will be appeared.
The following registry modification is required for creating a context menu in Windows Explorer.
Create a new key “OpenWith PicZoom
” under HKEY_CLASSES_ROOT\*\Shell\.
RegistryHandler::RegisterForAllFiles()
is responsible for creating a OpenWith PicZoom
entry in HKEY_CLASSES_ROOT\*\Shell\.
PicZoomInstaller
: Some other registry modification is also implemented, in order to create a new application entry(PicZoom
) in open with list.
The registry modification is identified by trial and error method. I selected a new program(PicZoom.exe) as default application for opening a BMP file. Then I searched registry and found out the registry location to create an application in open with list. I don’t know any other method to do the same.
The registry entries are created for open with list in bmp, jpg, png, and tga files.
RegistryHandler::AddApplicationName()
is responsible for creating PicZoom
in open with list of different image files.
When retrieving the width and height of a character with GetGlyphOutline()
, I got a GDI_ERROR
. Debug mode works fine, but release mode cause a GDI_ERROR
. At last, I found its reason from some forums. The transformation matrix provided to , should be initialised with identity matrix.
Projection Matrix
Since there is no 3D related operation, here we can use orthographic projection. Orthographic projection area is same as the window coordinate, and simply overcame -1 to +1 mapping method of perspective projection.
CPU Usage
Initially I prepared a timer, which will display image in 60 frames per second. This causes high CPU usage of PicZoom
without any operation :). That was not good at all. I just prepared different timers for different tasks. For example, when zoom starts, ::SetTimer
() is called with TIMER_ZOOM
ID. Whenever zoom task is over, ImageArea
class will kill this timer, and avoid unwanted draw of image to screen.
Support for NonPowerOfTwo Textures
By default, opengl texture dimension should satisfy power of 2. The width and height of texture should be a power of 2. This can be avoided if your machine supports GL_ARB_texture_non_power_of_two
extension.
Initially, GLExtension
retrieves the status of GL_ARB_texture_non_power_of_two
and updates GLExtension::m_bNonPowerOfTwo
. When creating a texture, GLTexture
uses this member and decides texture dimension should satisfy non power of two.
bool GLTexture::Create(int nWidth, int nHeight, void *pbyData,
int nFormat_i, int nInternalFormat_i)
{
bool bNonPowerTwo = (GLExtension::GetInstance()).m_bNonPowerOfTwo;;
if( bNonPowerTwo )
{
glTexImage2D( GL_TEXTURE_2D, 0, nInternalFormat_i, nWidth, nHeight, 0,
nFormat_i, GL_UNSIGNED_BYTE, pbyData );
}
else
{
int nNewWidth = PicZoomUtil::GetNearestPowerOf2( nWidth );
int nNewHeight = PicZoomUtil::GetNearestPowerOf2( nHeight );
int nChannelCount = ( GL_RGB8 == nInternalFormat_i ) ? 3 : 4;
int nSize = nNewWidth * nNewHeight * nChannelCount;
if( 0 != ( nNewWidth * nChannelCount ) % 4 )
{
nSize = nNewHeight * ceil( ( nNewWidth * nChannelCount ) / 4.0f ) * 4.0f;
}
BYTE* pbyDataNew = new BYTE[nSize];
memset( pbyDataNew, 0, nSize );
glTexImage2D( GL_TEXTURE_2D, 0, nInternalFormat_i, nNewWidth, nNewHeight, 0,
nFormat_i, GL_UNSIGNED_BYTE, pbyDataNew );
glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, nWidth, nHeight, nFormat_i,
GL_UNSIGNED_BYTE, pbyData );
delete[] pbyDataNew;
}
}
Since all images are created as texture, PicZoom
requires some graphics memory. In some machines, PicZoom
is not starting, the preparation of background image [Desktop background texture] fails. I checked different machines with graphics card, and none of them cause error to start PicZoom
. But some machines without graphics card failed to start. When stating PicZoom
, an error message similar to the below one may appear, if your machine does not have enough graphics memory to prepare the textures.
I checked the reason of texture creation failure, in a machine without graphics card. Since some OpenGL applications with multi-texturing are perfectly running in those machines. I changed width and height of these textures to higher values [Modified multi-texturing application to display bitmap of size 1024]. Then I got white rectangle display, instead of proper texture image. I hope this issue is caused by lack of graphic memory to prepare big sized textures.
- 16-Jan-2011: Initial version
- 24-Jan-2011: Added details of classes used in
PicZoom
- 16-Feb-2011: Modified
InstallPicZoom
to handle uninstall functionality