From fac3ffe7c8fed8b6174857dadd0bf9349f06b4c5 Mon Sep 17 00:00:00 2001 From: Gogs Date: Sat, 31 Mar 2018 14:50:54 +0100 Subject: [PATCH] Updated targa reader to support screen origin bit. --- .../gil/extension/io/formats/targa/read.hpp | 8 +- .../io/formats/targa/reader_backend.hpp | 14 +- .../io/formats/targa/scanline_read.hpp | 5 + io/test/targa_read_test.cpp | 124 ++++++++++++++++-- .../targa/24BPP_compressed_ul_origin.tga | Bin 0 -> 4211 bytes .../targa/24BPP_uncompressed_ul_origin.tga | Bin 0 -> 46172 bytes .../targa/32BPP_compressed_ul_origin.tga | Bin 0 -> 4920 bytes .../targa/32BPP_uncompressed_ul_origin.tga | Bin 0 -> 61548 bytes 8 files changed, 127 insertions(+), 24 deletions(-) create mode 100644 io/test_images/targa/24BPP_compressed_ul_origin.tga create mode 100644 io/test_images/targa/24BPP_uncompressed_ul_origin.tga create mode 100644 io/test_images/targa/32BPP_compressed_ul_origin.tga create mode 100644 io/test_images/targa/32BPP_uncompressed_ul_origin.tga diff --git a/include/boost/gil/extension/io/formats/targa/read.hpp b/include/boost/gil/extension/io/formats/targa/read.hpp index e8a3f39c1..e3d9f5ff2 100644 --- a/include/boost/gil/extension/io/formats/targa/read.hpp +++ b/include/boost/gil/extension/io/formats/targa/read.hpp @@ -127,7 +127,7 @@ public: { this->_scanline_length = this->_info._width * ( this->_info._bits_per_pixel / 8 ); - if( this->_info._descriptor & 0x20 ) + if( this->_info._screen_origin_bit ) { read_data< bgr8_view_t >( flipped_up_down_view( dst_view ) ); } @@ -142,7 +142,7 @@ public: { this->_scanline_length = this->_info._width * ( this->_info._bits_per_pixel / 8 ); - if( this->_info._descriptor & 0x20 ) + if( this->_info._screen_origin_bit ) { read_data< bgra8_view_t >( flipped_up_down_view( dst_view ) ); } @@ -178,7 +178,7 @@ public: { case 24: { - if( this->_info._descriptor & 0x20 ) + if( this->_info._screen_origin_bit ) { read_rle_data< bgr8_view_t >( flipped_up_down_view( dst_view ) ); } @@ -190,7 +190,7 @@ public: } case 32: { - if( this->_info._descriptor & 0x20 ) + if( this->_info._screen_origin_bit ) { read_rle_data< bgra8_view_t >( flipped_up_down_view( dst_view ) ); } diff --git a/include/boost/gil/extension/io/formats/targa/reader_backend.hpp b/include/boost/gil/extension/io/formats/targa/reader_backend.hpp index 750bc1455..251c90d92 100644 --- a/include/boost/gil/extension/io/formats/targa/reader_backend.hpp +++ b/include/boost/gil/extension/io/formats/targa/reader_backend.hpp @@ -94,7 +94,6 @@ public: } _info._descriptor = _io_dev.read_uint8(); - targa_descriptor::type pixel_type = _info._descriptor & 0xdf; // According to TGA specs, http://www.gamers.org/dEngine/quake3/TGA.txt, // the image descriptor byte is: @@ -104,7 +103,7 @@ public: { io_error("Unsupported descriptor for targa file"); } - else if (_info._bits_per_pixel == 24 && pixel_type != 0) + else if (_info._bits_per_pixel == 24) { // Bits 3-0 - For the Targa 24, it should be 0. if ((_info._descriptor & 0x0FU) != 0) @@ -112,24 +111,23 @@ public: io_error("Unsupported descriptor for targa file"); } } - else if (_info._bits_per_pixel == 32 && pixel_type != 0) + else if (_info._bits_per_pixel == 32) { // Bits 3-0 - For Targa 32, it should be 8. if (_info._descriptor != 8 && _info._descriptor != 40) { io_error("Unsupported descriptor for targa file"); } - - if (_info._descriptor & 32) - { - _info._screen_origin_bit = true; - } } else { io_error("Unsupported descriptor for targa file"); } + if (_info._descriptor & 32) + { + _info._screen_origin_bit = true; + } _info._valid = true; } diff --git a/include/boost/gil/extension/io/formats/targa/scanline_read.hpp b/include/boost/gil/extension/io/formats/targa/scanline_read.hpp index 78426e017..014c46b2e 100644 --- a/include/boost/gil/extension/io/formats/targa/scanline_read.hpp +++ b/include/boost/gil/extension/io/formats/targa/scanline_read.hpp @@ -120,6 +120,11 @@ private: { io_error( "Non-indexed targa files containing a palette are not supported." ); } + + if( this->_info._screen_origin_bit ) + { + io_error( "scanline reader cannot read targa files which have screen origin bit set." ); + } switch( this->_info._bits_per_pixel ) { diff --git a/io/test/targa_read_test.cpp b/io/test/targa_read_test.cpp index 02dc44592..e8386432b 100644 --- a/io/test/targa_read_test.cpp +++ b/io/test/targa_read_test.cpp @@ -75,10 +75,17 @@ BOOST_AUTO_TEST_CASE( read_reference_images_test ) // 24BPP_compressed.tga { rgb8_image_t img; - read_image( targa_in + "24BPP_compressed.tga", img, tag_t() ); - BOOST_CHECK_EQUAL( view( img ).width() , 124 ); - BOOST_CHECK_EQUAL( view( img ).height(), 124 ); + + typename rgb8_image_t::x_coord_t width = view( img ).width(); + typename rgb8_image_t::y_coord_t height = view( img ).height(); + + BOOST_CHECK_EQUAL( width , 124 ); + BOOST_CHECK_EQUAL( height, 124 ); + BOOST_CHECK( view( img )(0, 0) == rgb8_pixel_t(248, 0, 248) ); + BOOST_CHECK( view( img )(width-1, 0) == rgb8_pixel_t(0, 0, 248) ); + BOOST_CHECK( view( img )(0, height-1) == rgb8_pixel_t(248, 0, 0) ); + BOOST_CHECK( view( img )(width-1, height-1) == rgb8_pixel_t(248, 0, 248) ); write( img, "24BPP_compressed_out.tga" ); } @@ -86,10 +93,17 @@ BOOST_AUTO_TEST_CASE( read_reference_images_test ) // 24BPP_uncompressed.tga { rgb8_image_t img; - read_image( targa_in + "24BPP_uncompressed.tga", img, tag_t() ); - BOOST_CHECK_EQUAL( view( img ).width() , 124 ); - BOOST_CHECK_EQUAL( view( img ).height(), 124 ); + + typename rgb8_image_t::x_coord_t width = view( img ).width(); + typename rgb8_image_t::y_coord_t height = view( img ).height(); + + BOOST_CHECK_EQUAL( width , 124 ); + BOOST_CHECK_EQUAL( height, 124 ); + BOOST_CHECK( view( img )(0, 0) == rgb8_pixel_t(248, 0, 248) ); + BOOST_CHECK( view( img )(width-1, 0) == rgb8_pixel_t(0, 0, 248) ); + BOOST_CHECK( view( img )(0, height-1) == rgb8_pixel_t(248, 0, 0) ); + BOOST_CHECK( view( img )(width-1, height-1) == rgb8_pixel_t(248, 0, 248) ); write( img, "24BPP_uncompressed_out.tga" ); @@ -99,10 +113,17 @@ BOOST_AUTO_TEST_CASE( read_reference_images_test ) // 32BPP_compressed.tga { rgba8_image_t img; - read_image( targa_in + "32BPP_compressed.tga", img, tag_t() ); - BOOST_CHECK_EQUAL( view( img ).width() , 124 ); - BOOST_CHECK_EQUAL( view( img ).height(), 124 ); + + typename rgba8_image_t::x_coord_t width = view( img ).width(); + typename rgba8_image_t::y_coord_t height = view( img ).height(); + + BOOST_CHECK_EQUAL( width , 124 ); + BOOST_CHECK_EQUAL( height, 124 ); + BOOST_CHECK( view( img )(0, 0) == rgba8_pixel_t(248, 0, 248, 255) ); + BOOST_CHECK( view( img )(width-1, 0) == rgba8_pixel_t(0, 0, 248, 255) ); + BOOST_CHECK( view( img )(0, height-1) == rgba8_pixel_t(0, 0, 0, 0) ); + BOOST_CHECK( view( img )(width-1, height-1) == rgba8_pixel_t(248, 0, 248, 255) ); write( img, "32BPP_compressed_out.tga" ); } @@ -110,15 +131,94 @@ BOOST_AUTO_TEST_CASE( read_reference_images_test ) // 32BPP_uncompressed.tga { rgba8_image_t img; - read_image( targa_in + "32BPP_uncompressed.tga", img, tag_t() ); - BOOST_CHECK_EQUAL( view( img ).width() , 124 ); - BOOST_CHECK_EQUAL( view( img ).height(), 124 ); + + typename rgba8_image_t::x_coord_t width = view( img ).width(); + typename rgba8_image_t::y_coord_t height = view( img ).height(); + + BOOST_CHECK_EQUAL( width , 124 ); + BOOST_CHECK_EQUAL( height, 124 ); + BOOST_CHECK( view( img )(0, 0) == rgba8_pixel_t(248, 0, 248, 255) ); + BOOST_CHECK( view( img )(width-1, 0) == rgba8_pixel_t(0, 0, 248, 255) ); + BOOST_CHECK( view( img )(0, height-1) == rgba8_pixel_t(0, 0, 0, 0) ); + BOOST_CHECK( view( img )(width-1, height-1) == rgba8_pixel_t(248, 0, 248, 255) ); write( img, "32BPP_uncompressed_out.tga" ); test_targa_scanline_reader< bgra8_image_t >( "32BPP_uncompressed.tga" ); } + + // 24BPP_compressed_ul_origin.tga + { + rgb8_image_t img; + read_image( targa_in + "24BPP_compressed_ul_origin.tga", img, tag_t() ); + + typename rgb8_image_t::x_coord_t width = view( img ).width(); + typename rgb8_image_t::y_coord_t height = view( img ).height(); + + BOOST_CHECK_EQUAL( width , 124 ); + BOOST_CHECK_EQUAL( height, 124 ); + BOOST_CHECK( view( img )(0, 0) == rgb8_pixel_t(248, 0, 248) ); + BOOST_CHECK( view( img )(width-1, 0) == rgb8_pixel_t(0, 0, 248) ); + BOOST_CHECK( view( img )(0, height-1) == rgb8_pixel_t(248, 0, 0) ); + BOOST_CHECK( view( img )(width-1, height-1) == rgb8_pixel_t(248, 0, 248) ); + + write( img, "24BPP_compressed_ul_origin_out.tga" ); + } + + // 24BPP_uncompressed_ul_origin.tga + { + rgb8_image_t img; + read_image( targa_in + "24BPP_uncompressed_ul_origin.tga", img, tag_t() ); + + typename rgb8_image_t::x_coord_t width = view( img ).width(); + typename rgb8_image_t::y_coord_t height = view( img ).height(); + + BOOST_CHECK_EQUAL( width , 124 ); + BOOST_CHECK_EQUAL( height, 124 ); + BOOST_CHECK( view( img )(0, 0) == rgb8_pixel_t(248, 0, 248) ); + BOOST_CHECK( view( img )(width-1, 0) == rgb8_pixel_t(0, 0, 248) ); + BOOST_CHECK( view( img )(0, height-1) == rgb8_pixel_t(248, 0, 0) ); + BOOST_CHECK( view( img )(width-1, height-1) == rgb8_pixel_t(248, 0, 248) ); + + write( img, "24BPP_uncompressed_ul_origin_out.tga" ); + } + + // 32BPP_compressed_ul_origin.tga + { + rgba8_image_t img; + read_image( targa_in + "32BPP_compressed_ul_origin.tga", img, tag_t() ); + + typename rgba8_image_t::x_coord_t width = view( img ).width(); + typename rgba8_image_t::y_coord_t height = view( img ).height(); + + BOOST_CHECK_EQUAL( width , 124 ); + BOOST_CHECK_EQUAL( height, 124 ); + BOOST_CHECK( view( img )(0, 0) == rgba8_pixel_t(248, 0, 248, 255) ); + BOOST_CHECK( view( img )(width-1, 0) == rgba8_pixel_t(0, 0, 248, 255) ); + BOOST_CHECK( view( img )(0, height-1) == rgba8_pixel_t(0, 0, 0, 0) ); + BOOST_CHECK( view( img )(width-1, height-1) == rgba8_pixel_t(248, 0, 248, 255) ); + + write( img, "32BPP_compressed_ul_origin_out.tga" ); + } + + // 32BPP_uncompressed_ul_origin.tga + { + rgba8_image_t img; + read_image( targa_in + "32BPP_uncompressed_ul_origin.tga", img, tag_t() ); + + typename rgba8_image_t::x_coord_t width = view( img ).width(); + typename rgba8_image_t::y_coord_t height = view( img ).height(); + + BOOST_CHECK_EQUAL( width , 124 ); + BOOST_CHECK_EQUAL( height, 124 ); + BOOST_CHECK( view( img )(0, 0) == rgba8_pixel_t(248, 0, 248, 255) ); + BOOST_CHECK( view( img )(width-1, 0) == rgba8_pixel_t(0, 0, 248, 255) ); + BOOST_CHECK( view( img )(0, height-1) == rgba8_pixel_t(0, 0, 0, 0) ); + BOOST_CHECK( view( img )(width-1, height-1) == rgba8_pixel_t(248, 0, 248, 255) ); + + write( img, "32BPP_uncompressed_ul_origin_out.tga" ); + } } BOOST_AUTO_TEST_CASE( partial_image_test ) diff --git a/io/test_images/targa/24BPP_compressed_ul_origin.tga b/io/test_images/targa/24BPP_compressed_ul_origin.tga new file mode 100644 index 0000000000000000000000000000000000000000..98988a75159baaf30f11a8590d40cad51248edc5 GIT binary patch literal 4211 zcmZQz;9`J)8U_%QP`LPm;m5omKNz}xFfh#f!SDkl+XAJVp>*pHh96A~3=DHY^bfES zhNd4s7(il;P=3pg9}G+k3=9x%GlXIQ(Jc%N42=*?49!qm8W|WEntm|+fYD%`V9Q~4 zf@DE%nS*2}%r21KAUSM&h=~yEK(>R_foz4?0ONst3Q>v84KTZ5;V>KO2Cx}mquZeV zLv{&57UnMe_OpRP3+!h|FhfX?-C&czG9Vtv%?mMH(2f$`Aa{UufnA4eKeo`vZgV5V z0pNH6*@?^t#WZFpbU;G^=1-8l2v@*Fv4=ixH$Yts_6hD7pu}!u-+_Dyb~8A{0Vk4IA?&;7DAi>k~2UC!gBzK1G5E0W4ZxkHz?e(c z{AgqN!O#pUHyD0^{n5zqg8?K5qgz1oKOkyA{=;S_NPRQZ4v?9xXl5`F;DhV{nGG@r zl(J#;9B3H_az7|sK{QAX*)EV?kQg>QL2(MR8?5dJ#BZQ71!OkV-#^g91gw^Ufq_&r z=R(7v2})ygCn#KD?gH5h;)C1|awkX}Y&ViSi7~SY8onSiU}k}6O3cRQcXTt6`5<>= zi$i4hgZv9hd#wx%42#gx9>^?^KcH~}PJ0%w}K!=lez|4Qe8Q(&u6@AAh;o#qa~%N`uuKpjriF9w-fi%mdLN^FVYn1Ek#p z6JLbGe27|zy_A~YN}${XnGecS3_lotfM{Iq2ZaaBAIR-@B6YP}}?_28JJP zpb8G0HW(N{X%IO~K;Z`pV^FyX69@Spqy{o!U;r!;t>?4Fg_@pTA*PI zibIe%jGl*7K7;gv)WT>`yo2~4vp_UVycJZ@GBAMJOW?*J$ZU{&8&rG&4l_Y5Plytj z4w%WLnh5d%%ysz80;vP}6qH+GuBW~0Kw$y0je7otnFyj`{)UAe$R?0DIbjIW3ra1p rFa((a3o{VS2#GFi5j#+s4dhi24haf%4f724^!L+^aP#zW)nfnvdUhc; literal 0 HcmV?d00001 diff --git a/io/test_images/targa/24BPP_uncompressed_ul_origin.tga b/io/test_images/targa/24BPP_uncompressed_ul_origin.tga new file mode 100644 index 0000000000000000000000000000000000000000..e5cab05c0815cea7b5f50e64d393ba7ab0a269aa GIT binary patch literal 46172 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