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 PhotoViewer 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.
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. ThenPicZoomDlg
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:
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(){ // Get Desktop window. HWND hDesktop = ::GetDesktopWindow(); // Get temporary Dc for getting data from Desktop. 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); // Get data from Desktop to Bitmap. MemoryDC.BitBlt(0, 0, sz.cx, sz.cy, &dc, 0, 0, SRCCOPY); // Read RGB data of Desktop window Dc. int nStatus = ::GetDIBits( MemoryDC.m_hDC, (HBITMAP)BmpObj.m_hObject, 0, sz.cy, m_pBuffer, &stBitmapInfo,DIB_RGB_COLORS );}
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
, andCloseButton
] and adds to a vector named WindowList
. Whenever we get WM_PAINT
in dialog, Draw()
commands are passed to all the 4 components. Like
, all mouse messages are also passed to the components.Draw
()
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.
// Set Pixel Transfer to make semitransparent effect of Desktop.glPixelTransferf( GL_RED_SCALE, 0.75 );glPixelTransferf( GL_GREEN_SCALE, 0.75 );glPixelTransferf( GL_BLUE_SCALE, 0.75 );// Create Desktop Texture.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(); // Here draws the background image. 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 ); } // Find out zoom factor based on width of and height of image. 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 ); // Apply Zoom factor 15 times. then first single scroll make 15% zoom. 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 throughwglUseFontBitmaps()
function of OpenGL
.
bool FontEngine::Create( HDC hDeviceContext_i ){ VERIFY(m_pFont->CreateFont( 15, // nHeight 7, // nWidth 0, // nEscapement 0, // nOrientation FW_BOLD, // nWeight FALSE, // bItalic FALSE, // bUnderline 0, // cStrikeOut ANSI_CHARSET, // nCharSet OUT_DEFAULT_PRECIS, // nOutPrecision CLIP_DEFAULT_PRECIS, // nClipPrecision ANTIALIASED_QUALITY, // nQuality DEFAULT_PITCH, // nPitchAndFamily L"Arial")); // lpszFacename 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;}
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 functionglTranslatef
.
// Y value is -ve, only because opengl Y coordinate is//increasing from bottom to top,// But Y direction mouse movement received in Dialog is decreasing from bottom to top.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,// No rotation 270.0, // First Clockwise 180.0, // Second Clockwise 90.0 // 3rd clockwise};
Inside ImageArea::Draw()
, rotation angle is applied to z-order to achieve the required rotation.
/// Drawing of Image.bool ImageArea::Draw(){ // .................... // Apply rotation 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.
// Vertex buffer creation logic in ImageArea.bool ImageArea::SetupWindow(){ // Setup Vertex buffer and UnProject. int nHalfOfImageY = m_nImageHeight / 2; int nHalfOfImageX = m_nImageWidth / 2; /* 0--3 | | 1--2 */ m_pVertexBuffer->SetAt( 0, -nHalfOfImageX , nHalfOfImageY, 0.0f, 0.0f,1.0f); // Left Top corner m_pVertexBuffer->SetAt( 1, -nHalfOfImageX , -nHalfOfImageY, 0.0f, 0.0f,0.0f), // Left Bottom m_pVertexBuffer->SetAt( 2, nHalfOfImageX, -nHalfOfImageY, 0.0f, 1.0f,0.0f); // Right bottom m_pVertexBuffer->SetAt( 3, nHalfOfImageX, nHalfOfImageY, 0.0f, 1.0f,1.0f); // Right top /// ...................}
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.
GLButton
is designed to handle all operations related to one button. Bottomwindows
creates 9 instances ofGLButton
, and hold in a list, and all commands are passed to the button list.
/*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 Messageto send to parentWindow( PicZoomDlg) is also provide to this class.Whenever user press the button, this class will send message to PicZoomDlg.*/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 withBMPLoader
, and create GLTexture
with RGBA data.
// The texture and vertex buffer for rendering are setup in this function.bool GLButton::SetupButton(){ // Create Vertex buffer. m_pVertexBuffer = new GLVertexBuffer; if( 0 == m_pVertexBuffer ) { return false; } // Create Vertex buffer for rendering Quad image. m_pVertexBuffer->CreateQuadVertexBuffer(); UpdateVertexBuffer(); // Setup Texture from Bitmap resource ID. m_pTexture = new GLTexture; int nWidth = 0; int nHeight = 0; BYTE* pbyARGBImage = 0; BMPLoader LoadImage; // Load Alpha enabled texture. LoadImage.LoadBMP( m_nResourceID, nWidth, nHeight, pbyARGBImage, true ); // Create RGBA format texture. 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 ) { // After drawing, pixelstore biasing is changed. //glColor4f( 1.0, 1.0, 1.0, 1.0 ); glColor4f( m_fTransparency, m_fTransparency, m_fTransparency, m_fTransparency ); } else { // After drawing, pixelstore biasing is changed. //glColor4f( 0.75, 0.75, 0.75, 1.0 ); 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()) { // Hide old string. Here fColorComponent will decrease // after each frame. 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 ); // Drawing text to screen. 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; // Show New string. Here fColorComponent will // increase during each frame, then reach the maximum value. 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 ); // Drawing text to screen. 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 )) { // Here check the bottom left corner of circle. // If mouse move is not within the semi-circle, then return false. 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(); // CloseBoundry draws the outline of circle in 50% transparency. 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(){ // glColor3f is used to make small amount of texture display. // This color factor is multiplied with texel color and get a shading effect. glColor4f( fColorFactor, fColorFactor, fColorFactor, fColorFactor ); m_pTexture1->Enable(); // Apply zoom1. glScalef( m_fZoom1, m_fZoom1, 0.0 ); m_pVertexBuffer1->DrawVertexBuffer( GL_QUADS ); glPopMatrix(); if( nRemTime < 100 ) { glPushMatrix(); // When transparent display of second texture is required. float fTex2Color = 1.0 - fColorFactor; glColor4f( fTex2Color, fTex2Color, fTex2Color, fTex2Color ); m_pTexture2->Enable(); // Apply Zoom 2. 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, // Structure Identifier 0, // -1 to Drop more than one file szDroppedFile,// Dropped File Name 1023 *2 ); // Max char // Load new Image file. 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 ofPicZoom
. 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 thisElapseTime()
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 drawstring
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 toparentWindow( 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 inHKEY_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.
// HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.jpg// HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.bmp// HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.png// HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.tga
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.
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.
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.
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 updatesGLExtension::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){ // ............... // Retrieve Non power of two support and create texture based on it. 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 { // if non-power of two is not supported, need to create nearest n^2 texture. 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 ); // Set black data glTexImage2D( GL_TEXTURE_2D, 0, nInternalFormat_i, nNewWidth, nNewHeight, 0, nFormat_i, GL_UNSIGNED_BYTE, pbyDataNew ); // Update the required area with input data. 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.
PicZoom
InstallPicZoom
to handle uninstall functionality联系客服