1
/***************************************************************************
3
* This is a QGLWidget displaying an image or portion of an image in a *
6
* Author: Graeme van der Vlugt *
7
* Copyright: Imetric 3D GmbH *
11
* This program is free software; you can redistribute it and/or modify *
12
* it under the terms of the GNU Library General Public License as *
13
* published by the Free Software Foundation; either version 2 of the *
14
* License, or (at your option) any later version. *
15
* for detail see the LICENCE text file. *
17
***************************************************************************/
19
#include "PreCompiled.h"
21
#include "GLImageBox.h"
23
using namespace ImageGui;
25
#if defined(Q_CC_MSVC)
26
#pragma warning(disable:4305) // init: truncation from const double to float
29
/* TRANSLATOR ImageGui::GLImageBox */
32
GLImageBox::GLImageBox(QWidget * parent, const QGLWidget * shareWidget, Qt::WFlags f)
33
: QGLWidget(parent, shareWidget, f)
35
// uses default display format for the OpenGL rendering context
36
// (double buffering is enabled)
38
// enable mouse tracking when moving even if no buttons are pressed
39
setMouseTracking(true);
41
// initialise variables
53
GLImageBox::~GLImageBox()
62
// Set up the OpenGL rendering state
63
void GLImageBox::initializeGL()
65
qglClearColor( Qt::black ); // Let OpenGL clear to black
69
// Update the viewport
70
void GLImageBox::resizeGL( int w, int h )
72
glViewport( 0, 0, (GLint)w, (GLint)h );
73
glMatrixMode( GL_PROJECTION );
75
gluOrtho2D(0, width() - 1, height() - 1, 0);
76
glMatrixMode(GL_MODELVIEW);
79
// Redraw (current image)
80
void GLImageBox::redraw()
87
void GLImageBox::paintGL()
89
// clear background (in back buffer)
90
glDrawBuffer(GL_BACK);
91
glClear(GL_COLOR_BUFFER_BIT);
96
// Emit a signal for owners to draw any graphics that is needed.
97
if (_image.hasValidData() == true)
100
// flush the OpenGL graphical pipeline
103
// Double buffering is used so we need to swap the buffers
104
// There is no need to explicitly call this function because it is
105
// done automatically after each widget repaint, i.e. each time after paintGL() has been executed
110
void GLImageBox::drawImage()
112
if (_image.hasValidData() == false)
115
// Gets the size of the diplayed image area using the current display settings
116
// (in units of image pixels)
118
getDisplayedImageAreaSize(dx, dy);
120
// Draw the visible image region with the correct position and zoom
121
if ((dx > 0) && (dy > 0))
123
// Get top left image pixel to display
124
int tlx = std::max<int>(0, _x0);
125
int tly = std::max<int>(0, _y0);
127
// Get pointer to first pixel in source image rectangle
128
unsigned char* pPix = (unsigned char *)(_image.getPixelDataPtr());
129
pPix += (unsigned long)(_image.getNumBytesPerPixel()) * (tly * _image.getWidth() + tlx);
131
// Draw in the back buffer, using the following parameters
132
glDrawBuffer(GL_BACK);
133
glPixelStorei(GL_UNPACK_ROW_LENGTH, _image.getWidth()); // defines number of pixels in a row
134
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // defines byte alignment of rows
135
glPixelZoom(_zoomFactor, -_zoomFactor); // defines the zoom factors to draw at
137
// set current raster position to coincide with top left image pixel to display
138
// the first pixel is always displayed in full when zoomed in
139
// round to nearest widget pixel that coincides with top left corner of top left image pixel to display
140
int xx = (int)floor(ICToWC_X(tlx - 0.5) + 0.5);
141
int yy = (int)floor(ICToWC_Y(tly - 0.5) + 0.5);
142
glRasterPos2f(xx, yy);
144
// Compute scale to stretch number of significant bits to full range
145
// e.g. stretch 12 significant bits to 16-bit range: 0-4095 -> 0-65535, therefore scale = 65535/4095
146
double scale = (pow(2.0, _image.getNumBitsPerSample()) - 1.0) / (pow(2.0, _image.getNumSigBitsPerSample()) - 1.0);
147
glPixelTransferf(GL_RED_SCALE, (float)scale);
148
glPixelTransferf(GL_GREEN_SCALE, (float)scale);
149
glPixelTransferf(GL_BLUE_SCALE, (float)scale);
151
// Load the color map if present
154
glPixelTransferf(GL_MAP_COLOR, 1.0);
155
glPixelMapfv(GL_PIXEL_MAP_R_TO_R, _numMapEntries, _pColorMap);
156
glPixelMapfv(GL_PIXEL_MAP_G_TO_G, _numMapEntries, _pColorMap + _numMapEntries);
157
glPixelMapfv(GL_PIXEL_MAP_B_TO_B, _numMapEntries, _pColorMap + _numMapEntries * 2);
158
glPixelMapfv(GL_PIXEL_MAP_A_TO_A, _numMapEntries, _pColorMap + _numMapEntries * 3);
162
glPixelTransferf(GL_MAP_COLOR, 0.0);
165
glPixelMapfv(GL_PIXEL_MAP_R_TO_R, 1, &zero);
166
glPixelMapfv(GL_PIXEL_MAP_G_TO_G, 1, &zero);
167
glPixelMapfv(GL_PIXEL_MAP_B_TO_B, 1, &zero);
168
glPixelMapfv(GL_PIXEL_MAP_A_TO_A, 1, &one);
171
// Get the pixel format
174
getPixFormat(pixFormat, pixType);
176
// Draw the defined source rectangle
177
glDrawPixels(dx, dy, pixFormat, pixType, (GLvoid *)pPix);
182
// Gets the size of the diplayed image area using the current display settings
183
// (in units of image pixels)
184
void GLImageBox::getDisplayedImageAreaSize(int &dx, int &dy)
186
if (_image.hasValidData() == false)
193
// Make sure drawing position and zoom factor are valid
197
// Image coordinates of top left widget pixel = (_x0, _y0)
198
// Get image coordinates of bottom right widget pixel
199
int brx = (int)ceil(WCToIC_X(width() - 1));
200
int bry = (int)ceil(WCToIC_Y(height() - 1));
202
// Find the outer coordinates of the displayed image area
203
int itlx = std::max<int>(_x0, 0);
204
int itly = std::max<int>(_y0, 0);
205
int ibrx = std::min<int>(brx, (int)(_image.getWidth()) - 1);
206
int ibry = std::min<int>(bry, (int)(_image.getHeight()) - 1);
207
if ((itlx >= (int)(_image.getWidth())) ||
208
(itly >= (int)(_image.getHeight())) ||
215
dx = ibrx - itlx + 1;
216
dy = ibry - itly + 1;
220
// Gets the value of an image sample at the given image pixel position
221
// Returns 0 for valid value or -1 if coordinates or sample index are out of range or
222
// if there is no image data
223
int GLImageBox::getImageSample(int x, int y, unsigned short sampleIndex, double &value)
225
return (_image.getSample(x, y, sampleIndex, value));
228
// Gets the number of samples per pixel for the image
229
unsigned short GLImageBox::getImageNumSamplesPerPix()
231
return (_image.getNumSamples());
234
// Gets the format (color space format) of the image
235
int GLImageBox::getImageFormat()
237
return (_image.getFormat());
241
// Get the OpenGL pixel format and pixel type from the image properties
242
void GLImageBox::getPixFormat(GLenum &pixFormat, GLenum &pixType)
244
switch(_image.getFormat())
247
pixFormat = GL_LUMINANCE;
248
pixType = GL_UNSIGNED_BYTE;
251
pixFormat = GL_LUMINANCE;
252
pixType = GL_UNSIGNED_SHORT;
255
pixFormat = GL_LUMINANCE;
256
pixType = GL_UNSIGNED_INT;
260
pixType = GL_UNSIGNED_BYTE;
264
pixFormat = GL_BGR_EXT;
265
pixType = GL_UNSIGNED_BYTE;
269
pixType = GL_UNSIGNED_SHORT;
272
pixFormat = GL_BGR_EXT;
273
pixType = GL_UNSIGNED_SHORT;
278
pixType = GL_UNSIGNED_BYTE;
282
pixType = GL_UNSIGNED_SHORT;
286
pixFormat = GL_BGRA_EXT;
287
pixType = GL_UNSIGNED_BYTE;
290
pixFormat = GL_BGRA_EXT;
291
pixType = GL_UNSIGNED_SHORT;
295
// Should never happen
296
pixFormat = GL_LUMINANCE;
297
pixType = GL_UNSIGNED_BYTE;
298
QMessageBox::warning((QWidget *)this, tr("GLImageBox::getPixFormat"),
299
tr("Undefined type of colour space for image viewing"));
304
// Limits the current position (centre of top left image pixel)
305
// Currently we don't limit it!
306
void GLImageBox::limitCurrPos()
308
if (_image.hasValidData() == false)
314
else if (_x0 >= (int)(_image.getWidth()))
315
_x0 = _image.getWidth() - 1;
318
else if (_y0 >= (int)(_image.getHeight()))
319
_y0 = _image.getHeight() - 1;
323
// Limits the current zoom factor from 1:64 to 64:1
324
void GLImageBox::limitZoomFactor()
326
if (_zoomFactor > 64.0)
328
else if (_zoomFactor < (1.0 / 64.0))
329
_zoomFactor = 1.0 / 64.0;
332
// Set the current position (centre of top left image pixel coordinates)
333
// This function does not redraw (call redraw afterwards)
334
void GLImageBox::setCurrPos(int x0, int y0)
341
// Fixes a base position at the current position
342
void GLImageBox::fixBasePosCurr()
344
if (_image.hasValidData() == false)
356
// Set the current zoom factor
357
// Option to centre the zoom at a given image point or not
358
// This function does not redraw (call redraw afterwards)
359
void GLImageBox::setZoomFactor(double zoomFactor, bool useCentrePt, int ICx, int ICy)
361
if ((useCentrePt == false) || (_image.hasValidData() == false))
363
_zoomFactor = zoomFactor;
368
// Set new zoom factor
369
_zoomFactor = zoomFactor;
372
// get centre position of widget in image coordinates
374
getCentrePoint(ix, iy);
376
// try to shift the current position so that defined centre point is in the middle of the widget
377
// (this can be modified by the limitCurrPos function)
378
setCurrPos(_x0 - ix + ICx, _y0 - iy + ICy);
382
// Stretch or shrink the image to fit the view (although the zoom factor is limited so a
383
// very small or very big image may not fit completely (depending on the size of the view)
384
// This function redraws
385
void GLImageBox::stretchToFit()
387
if (_image.hasValidData() == false)
394
// Sets the settings needed to fit the image into the view (although the zoom factor is limited so a
395
// very small or very big image may not fit completely (depending on the size of the view)
396
// This function does not redraw (call redraw afterwards)
397
void GLImageBox::setToFit()
399
if (_image.hasValidData() == false)
402
// Compute ideal zoom factor to fit the image
403
double zoomX = (double)width() / (double)(_image.getWidth());
404
double zoomY = (double)height() / (double)(_image.getHeight());
411
// set current position to top left image pixel
415
// Sets the normal viewing position and zoom = 1
416
// If the image is smaller than the widget then the image is centred
417
// otherwise we view the top left part of the image
418
// This function does not redraw (call redraw afterwards)
419
void GLImageBox::setNormal()
421
if (_image.hasValidData() == false)
424
if (((int)(_image.getWidth()) < width()) && ((int)(_image.getHeight()) < height()))
426
setZoomFactor(1.0, true, _image.getWidth() / 2, _image.getHeight() / 2);
435
// Gets the image coordinates of the centre point of the widget
436
void GLImageBox::getCentrePoint(int &ICx, int &ICy)
438
ICx = (int)floor(WCToIC_X((double)(width() - 1) / 2.0) + 0.5);
439
ICy = (int)floor(WCToIC_Y((double)(height() - 1) / 2.0) + 0.5);
442
// Moves the image by a relative amount (in widget pixel units) from the base position
443
// First use fixBasePosCurr() to fix the base position at a position
444
void GLImageBox::relMoveWC(int WCdx, int WCdy)
446
double ICdx = WCdx / _zoomFactor;
447
double ICdy = WCdy / _zoomFactor;
448
setCurrPos(_base_x0 - (int)floor(ICdx + 0.5), _base_y0 - (int)floor(ICdy + 0.5));
452
// Computes an image x-coordinate from the widget x-coordinate
453
// Note: (_x0,_y0) is the centre of the image pixel displayed at the top left of the widget
454
// therefore (_x0 - 0.5, _y0 - 0.5) is the top left coordinate of this pixel which will
455
// theoretically coincide with widget coordinate (-0.5,-0.5)
456
// Zoom = 4: Widget(0,0) = Image(_x0 - 0.375,_y0 - 0.375)
457
// Zoom = 2: Widget(0,0) = Image(_x0 - 0.250,_y0 - 0.250)
458
// Zoom = 1: Widget(0,0) = Image(_x0,_y0)
459
// Zoom = 0.5: Widget(0,0) = Image(_x0 + 0.500,_y0 + 0.500)
460
// Zoom = 0.25: Widget(0,0) = Image(_x0 + 1.500,_y0 + 1.500)
461
double GLImageBox::WCToIC_X(double WidgetX)
463
return ((double)_x0 - 0.5 + (WidgetX + 0.5) / _zoomFactor);
466
// Computes an image y-coordinate from the widget y-coordinate
467
// Note: (_x0,_y0) is the centre of the image pixel displayed at the top left of the widget
468
// therefore (_x0 - 0.5, _y0 - 0.5) is the top left coordinate of this pixel which will
469
// theoretically coincide with widget coordinate (-0.5,-0.5)
470
// Zoom = 4: Widget(0,0) = Image(_x0 - 0.375,_y0 - 0.375)
471
// Zoom = 2: Widget(0,0) = Image(_x0 - 0.250,_y0 - 0.250)
472
// Zoom = 1: Widget(0,0) = Image(_x0,_y0)
473
// Zoom = 0.5: Widget(0,0) = Image(_x0 + 0.500,_y0 + 0.500)
474
// Zoom = 0.25: Widget(0,0) = Image(_x0 + 1.500,_y0 + 1.500)
475
double GLImageBox::WCToIC_Y(double WidgetY)
477
return ((double)_y0 - 0.5 + (WidgetY + 0.5) / _zoomFactor);
480
// Computes a widget x-coordinate from an image x-coordinate
481
// Note: (_x0,_y0) is the centre of the image pixel displayed at the top left of the widget
482
// therefore (_x0 - 0.5, _y0 - 0.5) is the top left coordinate of this pixel which will
483
// theoretically coincide with widget coordinate (-0.5,-0.5)
484
// Zoom = 4: Widget(0,0) = Image(_x0 - 0.375,_y0 - 0.375)
485
// Zoom = 2: Widget(0,0) = Image(_x0 - 0.250,_y0 - 0.250)
486
// Zoom = 1: Widget(0,0) = Image(_x0,_y0)
487
// Zoom = 0.5: Widget(0,0) = Image(_x0 + 0.500,_y0 + 0.500)
488
// Zoom = 0.25: Widget(0,0) = Image(_x0 + 1.500,_y0 + 1.500)
489
double GLImageBox::ICToWC_X(double ImageX)
491
return ((ImageX - (double)_x0 + 0.5) * _zoomFactor - 0.5);
494
// Computes a widget y-coordinate from an image y-coordinate
495
// Note: (_x0,_y0) is the centre of the image pixel displayed at the top left of the widget
496
// therefore (_x0 - 0.5, _y0 - 0.5) is the top left coordinate of this pixel which will
497
// theoretically coincide with widget coordinate (-0.5,-0.5)
498
// Zoom = 4: Widget(0,0) = Image(_x0 - 0.375,_y0 - 0.375)
499
// Zoom = 2: Widget(0,0) = Image(_x0 - 0.250,_y0 - 0.250)
500
// Zoom = 1: Widget(0,0) = Image(_x0,_y0)
501
// Zoom = 0.5: Widget(0,0) = Image(_x0 + 0.500,_y0 + 0.500)
502
// Zoom = 0.25: Widget(0,0) = Image(_x0 + 1.500,_y0 + 1.500)
503
double GLImageBox::ICToWC_Y(double ImageY)
505
return ((ImageY - (double)_y0 + 0.5) * _zoomFactor - 0.5);
509
// Clears the image data
510
void GLImageBox::clearImage()
516
// Load image by copying the pixel data
517
// The image object will take ownership of the copied pixel data
518
// (the source image is still controlled by the caller)
519
// If numSigBitsPerSample = 0 then the full range is assumed to be significant
520
// displayMode ... controls the initial display of the image, one of:
521
// IV_DISPLAY_NOCHANGE ... no change to view settings when displaying a new image
522
// IV_DISPLAY_FITIMAGE ... fit-image when displaying a new image (other settings remain the same)
523
// IV_DISPLAY_RESET ... reset settings when displaying a new image (image will be displayed at 1:1 scale with no color map)
524
// This function does not redraw (call redraw afterwards)
527
// -1 for invalid color format
528
// -2 for memory allocation error
529
int GLImageBox::createImageCopy(void* pSrcPixelData, unsigned long width, unsigned long height, int format, unsigned short numSigBitsPerSample, int displayMode)
532
int ret = _image.createCopy(pSrcPixelData, width, height, format, numSigBitsPerSample);
534
// Set display settings depending on mode
535
if (displayMode == IV_DISPLAY_RESET)
537
// reset drawing settings (position, scale, colour mapping) if requested
540
else if (displayMode == IV_DISPLAY_FITIMAGE)
542
// compute stretch to fit settings
545
else // if (displayMode == IV_DISPLAY_NOCHANGE)
554
// Make the image object point to another image source
555
// If takeOwnership is false then:
556
// This object will not own (control) or copy the pixel data
557
// (the source image is still controlled by the caller)
558
// Else if takeOwnership is true then:
559
// This object will take ownership (control) of the pixel data
560
// (the source image is not (should not be) controlled by the caller anymore)
561
// In this case the memory must have been allocated with the new operator (because this class will use the delete operator)
562
// If numSigBitsPerSample = 0 then the full range is assumed to be significant
563
// displayMode ... controls the initial display of the image, one of:
564
// IV_DISPLAY_NOCHANGE ... no change to view settings when displaying a new image
565
// IV_DISPLAY_FITIMAGE ... fit-image when displaying a new image (other settings remain the same)
566
// IV_DISPLAY_RESET ... reset settings when displaying a new image (image will be displayed at 1:1 scale with no color map)
567
// This function does not redraw (call redraw afterwards)
570
// -1 for invalid color format
571
int GLImageBox::pointImageTo(void* pSrcPixelData, unsigned long width, unsigned long height, int format, unsigned short numSigBitsPerSample, bool takeOwnership, int displayMode)
574
int ret = _image.pointTo(pSrcPixelData, width, height, format, numSigBitsPerSample, takeOwnership);
576
// Set display settings depending on mode
577
if (displayMode == IV_DISPLAY_RESET)
579
// reset drawing settings (position, scale, colour mapping) if requested
582
else if (displayMode == IV_DISPLAY_FITIMAGE)
584
// compute stretch to fit settings
587
else // if (displayMode == IV_DISPLAY_NOCHANGE)
596
// Reset display settings
597
void GLImageBox::resetDisplay()
600
setNormal(); // re-draws as well
603
// Clears the color map
604
void GLImageBox::clearColorMap()
606
delete [] _pColorMap;
611
// Calculate the number of color map entries to use
612
int GLImageBox::calcNumColorMapEntries()
614
// Get the maximum number of map entries that the system supports
615
// Get the number of bits per sample for the image if it exists and compute the number of pixel values
616
// Return the fewer amount of entries
618
glGetIntegerv(GL_MAX_PIXEL_MAP_TABLE, &maxMapEntries);
619
int NumEntries = maxMapEntries;
620
if (_image.hasValidData() == true)
621
NumEntries = (int)std::min<double>(pow(2.0, (double)(_image.getNumSigBitsPerSample())), (double)maxMapEntries);
625
// Creates a color map (All red entries come first, then green, then blue, then alpha)
626
// returns 0 for OK, -1 for memory allocation error
627
// numRequestedEntries ... requested number of map entries (used if not greater than maximum possible or number of intensity values)
628
// Initialise ... flag to initialise the map to a linear scale or not
629
int GLImageBox::createColorMap(int numEntriesReq, bool Initialise)
631
// Get the number of map entries to use
632
int maxNumEntries = calcNumColorMapEntries();
634
if (numEntriesReq <= 0)
635
numEntries = maxNumEntries;
637
numEntries = std::min<int>(numEntriesReq, maxNumEntries);
639
// Clear and re-create the color map if it's not the desired size
640
if (numEntries != _numMapEntries)
643
_numMapEntries = numEntries;
645
// Create the color map (RGBA)
648
_pColorMap = new float[4 * _numMapEntries];
657
// Initialise the color map if requested
658
// (All red entries come first, then green, then blue, then alpha)
659
if (Initialise == true)
661
// For each RGB channel
663
for (int chan = 0; chan < 3; chan++)
665
for (int in = 0; in < _numMapEntries; in++)
667
_pColorMap[arrayIndex] = (float)in / (float)(_numMapEntries - 1);
672
for (int in = 0; in < _numMapEntries; in++)
674
_pColorMap[arrayIndex] = 1.0;
682
// Sets a color map RGBA value
683
// (All red entries come first, then green, then blue, then alpha)
684
// index ... index of color map RGBA entry
685
// red ... intensity value for this red entry (range 0 to 1)
686
// green ... intensity value for this green entry (range 0 to 1)
687
// blue ... intensity value for this blue entry (range 0 to 1)
688
// alpha ... value for this alpha entry (range 0 to 1)
689
int GLImageBox::setColorMapRGBAValue(int index, float red, float green, float blue, float alpha)
691
if ((index < 0) || (index >= _numMapEntries) ||
692
(red < 0.0) || (red > 1.0) ||
693
(green < 0.0) || (green > 1.0) ||
694
(blue < 0.0) || (blue > 1.0) ||
695
(alpha < 0.0) || (alpha > 1.0))
698
_pColorMap[index] = red;
699
_pColorMap[_numMapEntries + index] = green;
700
_pColorMap[_numMapEntries * 2 + index] = blue;
701
_pColorMap[_numMapEntries * 3 + index] = alpha;
705
// Sets a color map red value
706
// (All red entries come first, then green, then blue, then alpha)
707
// index ... index of color map red entry
708
// value ... intensity value for this red entry (range 0 to 1)
709
int GLImageBox::setColorMapRedValue(int index, float value)
711
if ((index < 0) || (index >= _numMapEntries) || (value < 0.0) || (value > 1.0))
714
_pColorMap[index] = value;
718
// Sets a color map green value
719
// (All red entries come first, then green, then blue, then alpha)
720
// index ... index of color map green entry
721
// value ... intensity value for this green entry (range 0 to 1)
722
int GLImageBox::setColorMapGreenValue(int index, float value)
724
if ((index < 0) || (index >= _numMapEntries) || (value < 0.0) || (value > 1.0))
727
_pColorMap[_numMapEntries + index] = value;
731
// Sets a color map blue value
732
// (All red entries come first, then green, then blue, then alpha)
733
// index ... index of color map blue entry
734
// value ... intensity value for this blue entry (range 0 to 1)
735
int GLImageBox::setColorMapBlueValue(int index, float value)
737
if ((index < 0) || (index >= _numMapEntries) || (value < 0.0) || (value > 1.0))
740
_pColorMap[_numMapEntries * 2 + index] = value;
744
// Sets a color map alpha value
745
// (All red entries come first, then green, then blue, then alpha)
746
// index ... index of color map alpha entry
747
// value ... value for this alpha entry (range 0 to 1)
748
int GLImageBox::setColorMapAlphaValue(int index, float value)
750
if ((index < 0) || (index >= _numMapEntries) || (value < 0.0) || (value > 1.0))
753
_pColorMap[_numMapEntries * 3 + index] = value;
757
// Helper function to convert a pixel's value (of a sample) to the color map index (i.e. the map index that will be used for that pixel value)
758
unsigned int GLImageBox::pixValToMapIndex(double PixVal)
760
if (_pColorMap != NULL)
762
double MaxVal = pow(2.0, _image.getNumBitsPerSample()) - 1.0;
763
double Scale = (pow(2.0, _image.getNumBitsPerSample()) - 1.0) / (pow(2.0, _image.getNumSigBitsPerSample()) - 1.0);
764
double PixVal01 = Scale * PixVal / MaxVal;
765
int numMapEntries = getNumColorMapEntries();
766
unsigned int MapIndex = (unsigned int)floor(0.5 + PixVal01 * (double)(numMapEntries - 1));
776
#include "moc_GLImageBox.cpp"