Skip to content

Commit 8c10de0

Browse files
Support reading pixel values from color 16 bit images (backport #699) (#702)
Signed-off-by: Ian Chen <[email protected]> Co-authored-by: Ian Chen <[email protected]>
1 parent e2fc459 commit 8c10de0

File tree

4 files changed

+138
-74
lines changed

4 files changed

+138
-74
lines changed

graphics/src/Image.cc

Lines changed: 82 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#endif
2020
#include <FreeImage.h>
2121

22+
#include <cstdint>
2223
#include <cstring>
2324
#include <string>
2425

@@ -37,7 +38,7 @@ namespace gz
3738
class Image::Implementation
3839
{
3940
/// \brief bitmap data
40-
public: FIBITMAP *bitmap;
41+
public: FIBITMAP *bitmap{nullptr};
4142

4243
/// \brief path name of the image file
4344
public: std::string fullName;
@@ -48,6 +49,12 @@ namespace gz
4849
public: void DataImpl(unsigned char **_data, unsigned int &_count,
4950
FIBITMAP *_img) const;
5051

52+
/// \brief Color type for this image
53+
public: FREE_IMAGE_COLOR_TYPE colorType{FIC_RGB};
54+
55+
/// \brief Image type, i.e. pixel format
56+
public: FREE_IMAGE_TYPE imageType{FIT_UNKNOWN};
57+
5158
/// \brief Implementation of Data, returns vector of bytes
5259
public: std::vector<unsigned char> DataImpl(FIBITMAP *_img) const;
5360

@@ -156,6 +163,9 @@ int Image::Load(const std::string &_filename)
156163
return -1;
157164
}
158165

166+
this->dataPtr->colorType = FreeImage_GetColorType(this->dataPtr->bitmap);
167+
this->dataPtr->imageType = FreeImage_GetImageType(this->dataPtr->bitmap);
168+
159169
return 0;
160170
}
161171

@@ -263,6 +273,8 @@ void Image::SetFromData(const unsigned char *_data,
263273
this->Height());
264274
FreeImage_Unload(toDelete);
265275
}
276+
this->dataPtr->colorType = FreeImage_GetColorType(this->dataPtr->bitmap);
277+
this->dataPtr->imageType = FreeImage_GetImageType(this->dataPtr->bitmap);
266278
}
267279

268280
//////////////////////////////////////////////////
@@ -291,6 +303,8 @@ void Image::SetFromCompressedData(unsigned char *_data,
291303
FIMEMORY *fiMem = FreeImage_OpenMemory(_data, _size);
292304
this->dataPtr->bitmap = FreeImage_LoadFromMemory(format, fiMem);
293305
FreeImage_CloseMemory(fiMem);
306+
this->dataPtr->colorType = FreeImage_GetColorType(this->dataPtr->bitmap);
307+
this->dataPtr->imageType = FreeImage_GetImageType(this->dataPtr->bitmap);
294308
}
295309
else
296310
{
@@ -521,16 +535,22 @@ math::Color Image::Pixel(unsigned int _x, unsigned int _y) const
521535
if (!this->Valid())
522536
return clr;
523537

524-
FREE_IMAGE_COLOR_TYPE type = FreeImage_GetColorType(this->dataPtr->bitmap);
538+
if (_x >= this->Width() || _y >= this->Height())
539+
{
540+
gzerr << "Image: Coordinates out of range["
541+
<< _x << ", " << _y << "] \n";
542+
return clr;
543+
}
525544

526-
if (type == FIC_RGB || type == FIC_RGBALPHA)
545+
if ((this->dataPtr->colorType == FIC_RGB ||
546+
this->dataPtr->colorType == FIC_RGBALPHA) &&
547+
(this->dataPtr->imageType == FIT_BITMAP))
527548
{
528549
RGBQUAD firgb;
529-
530550
if (FreeImage_GetPixelColor(this->dataPtr->bitmap, _x, _y, &firgb) == FALSE)
531551
{
532-
gzerr << "Image: Coordinates out of range["
533-
<< _x << " " << _y << "] \n";
552+
gzerr << "Failed to get pixel value at ["
553+
<< _x << ", " << _y << "] \n";
534554
return clr;
535555
}
536556
clr.Set(firgb.rgbRed / 255.0f, firgb.rgbGreen / 255.0f,
@@ -541,8 +561,8 @@ math::Color Image::Pixel(unsigned int _x, unsigned int _y) const
541561
if (this->dataPtr->PixelIndex(
542562
this->dataPtr->bitmap, _x, _y, clr) == FALSE)
543563
{
544-
gzerr << "Image: Coordinates out of range ["
545-
<< _x << " " << _y << "] \n";
564+
gzerr << "Failed to get pixel value at ["
565+
<< _x << ", " << _y << "] \n";
546566
return clr;
547567
}
548568
}
@@ -579,64 +599,20 @@ math::Color Image::AvgColor() const
579599
//////////////////////////////////////////////////
580600
math::Color Image::MaxColor() const
581601
{
582-
unsigned int x, y;
583-
math::Color clr;
584602
math::Color maxClr;
585603

586-
maxClr.Set(0, 0, 0, 0);
587-
588604
if (!this->Valid())
589-
return clr;
590-
591-
FREE_IMAGE_COLOR_TYPE type = FreeImage_GetColorType(this->dataPtr->bitmap);
592-
593-
if (type == FIC_RGB || type == FIC_RGBALPHA)
594-
{
595-
RGBQUAD firgb;
605+
return maxClr;
596606

597-
for (y = 0; y < this->Height(); y++)
598-
{
599-
for (x = 0; x < this->Width(); x++)
600-
{
601-
clr.Set(0, 0, 0, 0);
602-
603-
if (FALSE ==
604-
FreeImage_GetPixelColor(this->dataPtr->bitmap, x, y, &firgb))
605-
{
606-
gzerr << "Image: Coordinates out of range["
607-
<< x << " " << y << "] \n";
608-
continue;
609-
}
610-
clr.Set(firgb.rgbRed / 255.0f, firgb.rgbGreen / 255.0f,
611-
firgb.rgbBlue / 255.0f);
612-
613-
if (clr.R() + clr.G() + clr.B() > maxClr.R() + maxClr.G() + maxClr.B())
614-
{
615-
maxClr = clr;
616-
}
617-
}
618-
}
619-
}
620-
else
607+
maxClr.Set(0, 0, 0, 0);
608+
for (unsigned int y = 0; y < this->Height(); y++)
621609
{
622-
for (y = 0; y < this->Height(); y++)
610+
for (unsigned int x = 0; x < this->Width(); x++)
623611
{
624-
for (x = 0; x < this->Width(); x++)
612+
math::Color clr = this->Pixel(x, y);
613+
if (clr.R() + clr.G() + clr.B() > maxClr.R() + maxClr.G() + maxClr.B())
625614
{
626-
clr.Set(0, 0, 0, 0);
627-
628-
if (this->dataPtr->PixelIndex(
629-
this->dataPtr->bitmap, x, y, clr) == FALSE)
630-
{
631-
gzerr << "Image: Coordinates out of range ["
632-
<< x << " " << y << "] \n";
633-
continue;
634-
}
635-
636-
if (clr.R() + clr.G() + clr.B() > maxClr.R() + maxClr.G() + maxClr.B())
637-
{
638-
maxClr = clr;
639-
}
615+
maxClr = clr;
640616
}
641617
}
642618
}
@@ -651,9 +627,11 @@ BOOL Image::Implementation::PixelIndex(
651627
if (!_dib)
652628
return FALSE;
653629

654-
FREE_IMAGE_TYPE imageType = FreeImage_GetImageType(_dib);
630+
if (_x >= FreeImage_GetWidth(_dib) || _y >= FreeImage_GetHeight(_dib))
631+
return FALSE;
632+
655633
// 8 bit images
656-
if (imageType == FIT_BITMAP)
634+
if (this->imageType == FIT_BITMAP)
657635
{
658636
BYTE byteValue;
659637
// FreeImage_GetPixelIndex should also work with 1 and 4 bit images
@@ -669,21 +647,51 @@ BOOL Image::Implementation::PixelIndex(
669647
_color.Set(value, value, value);
670648
}
671649
// 16 bit images
672-
else if (imageType == FIT_UINT16)
650+
else if (this->imageType == FIT_UINT16)
673651
{
674-
if ((_x < FreeImage_GetWidth(_dib)) && (_y < FreeImage_GetHeight(_dib)))
675-
{
676-
WORD *bits = reinterpret_cast<WORD *>(FreeImage_GetScanLine(_dib, _y));
677-
uint16_t word = static_cast<uint16_t>(bits[_x]);
678-
// convert to float value between 0-1
679-
float value = word / static_cast<float>(math::MAX_UI16);
680-
_color.Set(value, value, value);
681-
}
682-
else
683-
{
684-
return FALSE;
685-
}
652+
WORD *bits = reinterpret_cast<WORD *>(FreeImage_GetScanLine(_dib, _y));
653+
uint16_t word = static_cast<uint16_t>(bits[_x]);
654+
// convert to float value between 0-1
655+
float value = word / static_cast<float>(math::MAX_UI16);
656+
_color.Set(value, value, value);
657+
}
658+
else if (this->imageType == FIT_INT16)
659+
{
660+
WORD *bits = reinterpret_cast<WORD *>(FreeImage_GetScanLine(_dib, _y));
661+
int16_t word = static_cast<int16_t>(bits[_x]);
662+
// convert to float value between 0-1
663+
float value = word / static_cast<float>(math::MAX_I16);
664+
_color.Set(value, value, value);
686665
}
666+
else if (this->imageType == FIT_RGB16)
667+
{
668+
const unsigned int channels = 3u;
669+
WORD *bits = reinterpret_cast<WORD *>(FreeImage_GetScanLine(_dib, _y));
670+
uint16_t r = static_cast<uint16_t>(bits[_x * channels]);
671+
uint16_t g = static_cast<uint16_t>(bits[_x * channels + 1]);
672+
uint16_t b = static_cast<uint16_t>(bits[_x * channels + 2]);
673+
// convert to float value between 0-1
674+
float valueR = r / static_cast<float>(math::MAX_UI16);
675+
float valueG = g / static_cast<float>(math::MAX_UI16);
676+
float valueB = b / static_cast<float>(math::MAX_UI16);
677+
_color.Set(valueR, valueG, valueB);
678+
}
679+
else if (this->imageType == FIT_RGBA16)
680+
{
681+
const unsigned int channels = 4u;
682+
WORD *bits = reinterpret_cast<WORD *>(FreeImage_GetScanLine(_dib, _y));
683+
uint16_t r = static_cast<uint16_t>(bits[_x * channels]);
684+
uint16_t g = static_cast<uint16_t>(bits[_x * channels + 1]);
685+
uint16_t b = static_cast<uint16_t>(bits[_x * channels + 2]);
686+
uint16_t a = static_cast<uint16_t>(bits[_x * channels + 3]);
687+
// convert to float value between 0-1
688+
float valueR = r / static_cast<float>(math::MAX_UI16);
689+
float valueG = g / static_cast<float>(math::MAX_UI16);
690+
float valueB = b / static_cast<float>(math::MAX_UI16);
691+
float valueA = a / static_cast<float>(math::MAX_UI16);
692+
_color.Set(valueR, valueG, valueB, valueA);
693+
}
694+
687695
return TRUE;
688696
}
689697

graphics/src/Image_TEST.cc

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*
1616
*/
1717
#include <fstream>
18+
#include <string>
1819

1920
#include <gtest/gtest.h>
2021

@@ -728,6 +729,61 @@ TEST_F(ImageTest, Grayscale)
728729
}
729730
}
730731

732+
733+
/////////////////////////////////////////////////
734+
TEST_F(ImageTest, Color16bit)
735+
{
736+
{
737+
common::Image img;
738+
std::string fileName = common::testing::TestFile("data",
739+
"rgb_16bit.png");
740+
EXPECT_EQ(0, img.Load(fileName));
741+
const unsigned int width = 4u;
742+
const unsigned int height = 4u;
743+
const unsigned int channels = 3u;
744+
const unsigned int bpp = channels * 16u;
745+
EXPECT_TRUE(img.Valid());
746+
EXPECT_EQ(width, img.Width());
747+
EXPECT_EQ(height, img.Height());
748+
EXPECT_EQ(bpp, img.BPP());
749+
EXPECT_EQ(width * bpp / 8u, img.Pitch());
750+
EXPECT_EQ(common::Image::PixelFormatType::RGB_INT16, img.PixelFormat());
751+
math::Color maxColor(0.0f, 0.0f, 0.847f);
752+
EXPECT_NEAR(maxColor.R(), img.MaxColor().R(), 1e-3);
753+
EXPECT_NEAR(maxColor.G(), img.MaxColor().G(), 1e-3);
754+
EXPECT_NEAR(maxColor.B(), img.MaxColor().B(), 1e-3);
755+
math::Color pixelWithMaxColor = img.Pixel(3, 1);
756+
EXPECT_NEAR(maxColor.R(), pixelWithMaxColor.R(), 1e-3);
757+
EXPECT_NEAR(maxColor.G(), pixelWithMaxColor.G(), 1e-3);
758+
EXPECT_NEAR(maxColor.B(), pixelWithMaxColor.B(), 1e-3);
759+
}
760+
{
761+
common::Image img;
762+
std::string fileName = common::testing::TestFile("data",
763+
"rgba_16bit.png");
764+
EXPECT_EQ(0, img.Load(fileName));
765+
const unsigned int width = 4u;
766+
const unsigned int height = 4u;
767+
const unsigned int channels = 4u;
768+
const unsigned int bpp = channels * 16u;
769+
EXPECT_TRUE(img.Valid());
770+
EXPECT_EQ(width, img.Width());
771+
EXPECT_EQ(height, img.Height());
772+
EXPECT_EQ(bpp, img.BPP());
773+
EXPECT_EQ(width * bpp / 8u, img.Pitch());
774+
math::Color maxColor(0.0f, 0.0f, 0.847f, 0.5f);
775+
EXPECT_NEAR(maxColor.R(), img.MaxColor().R(), 1e-3);
776+
EXPECT_NEAR(maxColor.G(), img.MaxColor().G(), 1e-3);
777+
EXPECT_NEAR(maxColor.B(), img.MaxColor().B(), 1e-3);
778+
EXPECT_NEAR(maxColor.A(), img.MaxColor().A(), 1e-3);
779+
math::Color pixelWithMaxColor = img.Pixel(3, 1);
780+
EXPECT_NEAR(maxColor.R(), pixelWithMaxColor.R(), 1e-3);
781+
EXPECT_NEAR(maxColor.G(), pixelWithMaxColor.G(), 1e-3);
782+
EXPECT_NEAR(maxColor.B(), pixelWithMaxColor.B(), 1e-3);
783+
EXPECT_NEAR(maxColor.A(), pixelWithMaxColor.A(), 1e-3);
784+
}
785+
}
786+
731787
using string_int2 = std::tuple<const char *, unsigned int, unsigned int>;
732788

733789
class ImagePerformanceTest : public ImageTest,

test/data/rgb_16bit.png

250 Bytes
Loading

test/data/rgba_16bit.png

266 Bytes
Loading

0 commit comments

Comments
 (0)