Skip to content

Commit 60de1e7

Browse files
committed
Handle color 16 bit images
Signed-off-by: Ian Chen <[email protected]>
1 parent 1da42be commit 60de1e7

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,11 +38,17 @@ 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;
4445

46+
/// \brief Color type for this image
47+
public: FREE_IMAGE_COLOR_TYPE colorType{FIC_RGB};
48+
49+
/// \brief Image type, i.e. pixel format
50+
public: FREE_IMAGE_TYPE imageType{FIT_UNKNOWN};
51+
4552
/// \brief Implementation of Data, returns vector of bytes
4653
public: std::vector<unsigned char> DataImpl(FIBITMAP *_img) const;
4754

@@ -150,6 +157,9 @@ int Image::Load(const std::string &_filename)
150157
return -1;
151158
}
152159

160+
this->dataPtr->colorType = FreeImage_GetColorType(this->dataPtr->bitmap);
161+
this->dataPtr->imageType = FreeImage_GetImageType(this->dataPtr->bitmap);
162+
153163
return 0;
154164
}
155165

@@ -257,6 +267,8 @@ void Image::SetFromData(const unsigned char *_data,
257267
this->Height());
258268
FreeImage_Unload(toDelete);
259269
}
270+
this->dataPtr->colorType = FreeImage_GetColorType(this->dataPtr->bitmap);
271+
this->dataPtr->imageType = FreeImage_GetImageType(this->dataPtr->bitmap);
260272
}
261273

262274
//////////////////////////////////////////////////
@@ -285,6 +297,8 @@ void Image::SetFromCompressedData(unsigned char *_data,
285297
FIMEMORY *fiMem = FreeImage_OpenMemory(_data, _size);
286298
this->dataPtr->bitmap = FreeImage_LoadFromMemory(format, fiMem);
287299
FreeImage_CloseMemory(fiMem);
300+
this->dataPtr->colorType = FreeImage_GetColorType(this->dataPtr->bitmap);
301+
this->dataPtr->imageType = FreeImage_GetImageType(this->dataPtr->bitmap);
288302
}
289303
else
290304
{
@@ -415,16 +429,22 @@ math::Color Image::Pixel(unsigned int _x, unsigned int _y) const
415429
if (!this->Valid())
416430
return clr;
417431

418-
FREE_IMAGE_COLOR_TYPE type = FreeImage_GetColorType(this->dataPtr->bitmap);
432+
if (_x >= this->Width() || _y >= this->Height())
433+
{
434+
gzerr << "Image: Coordinates out of range["
435+
<< _x << ", " << _y << "] \n";
436+
return clr;
437+
}
419438

420-
if (type == FIC_RGB || type == FIC_RGBALPHA)
439+
if ((this->dataPtr->colorType == FIC_RGB ||
440+
this->dataPtr->colorType == FIC_RGBALPHA) &&
441+
(this->dataPtr->imageType == FIT_BITMAP))
421442
{
422443
RGBQUAD firgb;
423-
424444
if (FreeImage_GetPixelColor(this->dataPtr->bitmap, _x, _y, &firgb) == FALSE)
425445
{
426-
gzerr << "Image: Coordinates out of range["
427-
<< _x << " " << _y << "] \n";
446+
gzerr << "Failed to get pixel value at ["
447+
<< _x << ", " << _y << "] \n";
428448
return clr;
429449
}
430450
clr.Set(firgb.rgbRed / 255.0f, firgb.rgbGreen / 255.0f,
@@ -435,8 +455,8 @@ math::Color Image::Pixel(unsigned int _x, unsigned int _y) const
435455
if (this->dataPtr->PixelIndex(
436456
this->dataPtr->bitmap, _x, _y, clr) == FALSE)
437457
{
438-
gzerr << "Image: Coordinates out of range ["
439-
<< _x << " " << _y << "] \n";
458+
gzerr << "Failed to get pixel value at ["
459+
<< _x << ", " << _y << "] \n";
440460
return clr;
441461
}
442462
}
@@ -473,64 +493,20 @@ math::Color Image::AvgColor() const
473493
//////////////////////////////////////////////////
474494
math::Color Image::MaxColor() const
475495
{
476-
unsigned int x, y;
477-
math::Color clr;
478496
math::Color maxClr;
479497

480-
maxClr.Set(0, 0, 0, 0);
481-
482498
if (!this->Valid())
483-
return clr;
484-
485-
FREE_IMAGE_COLOR_TYPE type = FreeImage_GetColorType(this->dataPtr->bitmap);
486-
487-
if (type == FIC_RGB || type == FIC_RGBALPHA)
488-
{
489-
RGBQUAD firgb;
499+
return maxClr;
490500

491-
for (y = 0; y < this->Height(); y++)
492-
{
493-
for (x = 0; x < this->Width(); x++)
494-
{
495-
clr.Set(0, 0, 0, 0);
496-
497-
if (FALSE ==
498-
FreeImage_GetPixelColor(this->dataPtr->bitmap, x, y, &firgb))
499-
{
500-
gzerr << "Image: Coordinates out of range["
501-
<< x << " " << y << "] \n";
502-
continue;
503-
}
504-
clr.Set(firgb.rgbRed / 255.0f, firgb.rgbGreen / 255.0f,
505-
firgb.rgbBlue / 255.0f);
506-
507-
if (clr.R() + clr.G() + clr.B() > maxClr.R() + maxClr.G() + maxClr.B())
508-
{
509-
maxClr = clr;
510-
}
511-
}
512-
}
513-
}
514-
else
501+
maxClr.Set(0, 0, 0, 0);
502+
for (unsigned int y = 0; y < this->Height(); y++)
515503
{
516-
for (y = 0; y < this->Height(); y++)
504+
for (unsigned int x = 0; x < this->Width(); x++)
517505
{
518-
for (x = 0; x < this->Width(); x++)
506+
math::Color clr = this->Pixel(x, y);
507+
if (clr.R() + clr.G() + clr.B() > maxClr.R() + maxClr.G() + maxClr.B())
519508
{
520-
clr.Set(0, 0, 0, 0);
521-
522-
if (this->dataPtr->PixelIndex(
523-
this->dataPtr->bitmap, x, y, clr) == FALSE)
524-
{
525-
gzerr << "Image: Coordinates out of range ["
526-
<< x << " " << y << "] \n";
527-
continue;
528-
}
529-
530-
if (clr.R() + clr.G() + clr.B() > maxClr.R() + maxClr.G() + maxClr.B())
531-
{
532-
maxClr = clr;
533-
}
509+
maxClr = clr;
534510
}
535511
}
536512
}
@@ -545,9 +521,11 @@ BOOL Image::Implementation::PixelIndex(
545521
if (!_dib)
546522
return FALSE;
547523

548-
FREE_IMAGE_TYPE imageType = FreeImage_GetImageType(_dib);
524+
if (_x >= FreeImage_GetWidth(_dib) || _y >= FreeImage_GetHeight(_dib))
525+
return FALSE;
526+
549527
// 8 bit images
550-
if (imageType == FIT_BITMAP)
528+
if (this->imageType == FIT_BITMAP)
551529
{
552530
BYTE byteValue;
553531
// FreeImage_GetPixelIndex should also work with 1 and 4 bit images
@@ -563,21 +541,51 @@ BOOL Image::Implementation::PixelIndex(
563541
_color.Set(value, value, value);
564542
}
565543
// 16 bit images
566-
else if (imageType == FIT_UINT16)
544+
else if (this->imageType == FIT_UINT16)
567545
{
568-
if ((_x < FreeImage_GetWidth(_dib)) && (_y < FreeImage_GetHeight(_dib)))
569-
{
570-
WORD *bits = reinterpret_cast<WORD *>(FreeImage_GetScanLine(_dib, _y));
571-
uint16_t word = static_cast<uint16_t>(bits[_x]);
572-
// convert to float value between 0-1
573-
float value = word / static_cast<float>(math::MAX_UI16);
574-
_color.Set(value, value, value);
575-
}
576-
else
577-
{
578-
return FALSE;
579-
}
546+
WORD *bits = reinterpret_cast<WORD *>(FreeImage_GetScanLine(_dib, _y));
547+
uint16_t word = static_cast<uint16_t>(bits[_x]);
548+
// convert to float value between 0-1
549+
float value = word / static_cast<float>(math::MAX_UI16);
550+
_color.Set(value, value, value);
580551
}
552+
else if (this->imageType == FIT_INT16)
553+
{
554+
WORD *bits = reinterpret_cast<WORD *>(FreeImage_GetScanLine(_dib, _y));
555+
int16_t word = static_cast<int16_t>(bits[_x]);
556+
// convert to float value between 0-1
557+
float value = word / static_cast<float>(math::MAX_I16);
558+
_color.Set(value, value, value);
559+
}
560+
else if (this->imageType == FIT_RGB16)
561+
{
562+
const unsigned int channels = 3u;
563+
WORD *bits = reinterpret_cast<WORD *>(FreeImage_GetScanLine(_dib, _y));
564+
uint16_t r = static_cast<uint16_t>(bits[_x * channels]);
565+
uint16_t g = static_cast<uint16_t>(bits[_x * channels + 1]);
566+
uint16_t b = static_cast<uint16_t>(bits[_x * channels + 2]);
567+
// convert to float value between 0-1
568+
float valueR = r / static_cast<float>(math::MAX_UI16);
569+
float valueG = g / static_cast<float>(math::MAX_UI16);
570+
float valueB = b / static_cast<float>(math::MAX_UI16);
571+
_color.Set(valueR, valueG, valueB);
572+
}
573+
else if (this->imageType == FIT_RGBA16)
574+
{
575+
const unsigned int channels = 4u;
576+
WORD *bits = reinterpret_cast<WORD *>(FreeImage_GetScanLine(_dib, _y));
577+
uint16_t r = static_cast<uint16_t>(bits[_x * channels]);
578+
uint16_t g = static_cast<uint16_t>(bits[_x * channels + 1]);
579+
uint16_t b = static_cast<uint16_t>(bits[_x * channels + 2]);
580+
uint16_t a = static_cast<uint16_t>(bits[_x * channels + 3]);
581+
// convert to float value between 0-1
582+
float valueR = r / static_cast<float>(math::MAX_UI16);
583+
float valueG = g / static_cast<float>(math::MAX_UI16);
584+
float valueB = b / static_cast<float>(math::MAX_UI16);
585+
float valueA = a / static_cast<float>(math::MAX_UI16);
586+
_color.Set(valueR, valueG, valueB, valueA);
587+
}
588+
581589
return TRUE;
582590
}
583591

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

@@ -673,6 +674,61 @@ TEST_F(ImageTest, Grayscale)
673674
}
674675
}
675676

677+
678+
/////////////////////////////////////////////////
679+
TEST_F(ImageTest, Color16bit)
680+
{
681+
{
682+
common::Image img;
683+
std::string fileName = common::testing::TestFile("data",
684+
"rgb_16bit.png");
685+
EXPECT_EQ(0, img.Load(fileName));
686+
const unsigned int width = 4u;
687+
const unsigned int height = 4u;
688+
const unsigned int channels = 3u;
689+
const unsigned int bpp = channels * 16u;
690+
EXPECT_TRUE(img.Valid());
691+
EXPECT_EQ(width, img.Width());
692+
EXPECT_EQ(height, img.Height());
693+
EXPECT_EQ(bpp, img.BPP());
694+
EXPECT_EQ(width * bpp / 8u, img.Pitch());
695+
EXPECT_EQ(common::Image::PixelFormatType::RGB_INT16, img.PixelFormat());
696+
math::Color maxColor(0.0f, 0.0f, 0.847f);
697+
EXPECT_NEAR(maxColor.R(), img.MaxColor().R(), 1e-3);
698+
EXPECT_NEAR(maxColor.G(), img.MaxColor().G(), 1e-3);
699+
EXPECT_NEAR(maxColor.B(), img.MaxColor().B(), 1e-3);
700+
math::Color pixelWithMaxColor = img.Pixel(3, 1);
701+
EXPECT_NEAR(maxColor.R(), pixelWithMaxColor.R(), 1e-3);
702+
EXPECT_NEAR(maxColor.G(), pixelWithMaxColor.G(), 1e-3);
703+
EXPECT_NEAR(maxColor.B(), pixelWithMaxColor.B(), 1e-3);
704+
}
705+
{
706+
common::Image img;
707+
std::string fileName = common::testing::TestFile("data",
708+
"rgba_16bit.png");
709+
EXPECT_EQ(0, img.Load(fileName));
710+
const unsigned int width = 4u;
711+
const unsigned int height = 4u;
712+
const unsigned int channels = 4u;
713+
const unsigned int bpp = channels * 16u;
714+
EXPECT_TRUE(img.Valid());
715+
EXPECT_EQ(width, img.Width());
716+
EXPECT_EQ(height, img.Height());
717+
EXPECT_EQ(bpp, img.BPP());
718+
EXPECT_EQ(width * bpp / 8u, img.Pitch());
719+
math::Color maxColor(0.0f, 0.0f, 0.847f, 0.5f);
720+
EXPECT_NEAR(maxColor.R(), img.MaxColor().R(), 1e-3);
721+
EXPECT_NEAR(maxColor.G(), img.MaxColor().G(), 1e-3);
722+
EXPECT_NEAR(maxColor.B(), img.MaxColor().B(), 1e-3);
723+
EXPECT_NEAR(maxColor.A(), img.MaxColor().A(), 1e-3);
724+
math::Color pixelWithMaxColor = img.Pixel(3, 1);
725+
EXPECT_NEAR(maxColor.R(), pixelWithMaxColor.R(), 1e-3);
726+
EXPECT_NEAR(maxColor.G(), pixelWithMaxColor.G(), 1e-3);
727+
EXPECT_NEAR(maxColor.B(), pixelWithMaxColor.B(), 1e-3);
728+
EXPECT_NEAR(maxColor.A(), pixelWithMaxColor.A(), 1e-3);
729+
}
730+
}
731+
676732
using string_int2 = std::tuple<const char *, unsigned int, unsigned int>;
677733

678734
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)