Skip to content

Commit b700dc0

Browse files
bors[bot]Barugon
andauthored
Merge #246
246: Implement ColorTable struct r=ChristianBeilschmidt,lnicola a=Barugon Implement a `ColorTable` struct and add a `RasterBand::color_table` method. - [x] I agree to follow the project's [code of conduct](https://github.com/georust/gdal/blob/master/CODE_OF_CONDUCT.md). - [x] I added an entry to `CHANGES.md`. --- Co-authored-by: Barugon <[email protected]>
2 parents b0ebd1d + 73f5fcd commit b700dc0

File tree

5 files changed

+201
-3
lines changed

5 files changed

+201
-3
lines changed

CHANGES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727

2828
- <https://github.com/georust/gdal/pull/257>
2929

30+
- Add a `ColorTable` struct and `RasterBand::color_table` method
31+
32+
- <https://github.com/georust/gdal/pull/246>
33+
3034
## 0.12
3135

3236
- Bump Rust edition to 2021

fixtures/test_color_table.tif

22.1 KB
Binary file not shown.

src/raster/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ mod rasterize;
55
mod types;
66
mod warp;
77

8-
pub use rasterband::{Buffer, ByteBuffer, ColorInterpretation, RasterBand, ResampleAlg};
8+
pub use rasterband::{
9+
Buffer, ByteBuffer, CmykEntry, ColorEntry, ColorInterpretation, ColorTable, GrayEntry,
10+
HlsEntry, PaletteInterpretation, RasterBand, ResampleAlg, RgbaEntry,
11+
};
912
pub use rasterize::{rasterize, BurnSource, MergeAlgorithm, OptimizeMode, RasterizeOptions};
1013
pub use types::{GDALDataType, GdalType};
1114
pub use warp::reproject;

src/raster/rasterband.rs

Lines changed: 153 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ use crate::metadata::Metadata;
44
use crate::raster::{GDALDataType, GdalType};
55
use crate::utils::{_last_cpl_err, _last_null_pointer_err, _string};
66
use gdal_sys::{
7-
self, CPLErr, GDALColorInterp, GDALMajorObjectH, GDALRWFlag, GDALRasterBandH,
8-
GDALRasterIOExtraArg,
7+
self, CPLErr, GDALColorEntry, GDALColorInterp, GDALColorTableH, GDALMajorObjectH,
8+
GDALPaletteInterp, GDALRWFlag, GDALRasterBandH, GDALRasterIOExtraArg,
99
};
1010
use libc::c_int;
1111
use std::ffi::CString;
12+
use std::marker::PhantomData;
1213

1314
#[cfg(feature = "ndarray")]
1415
use ndarray::Array2;
@@ -421,6 +422,15 @@ impl<'a> RasterBand<'a> {
421422
Ok(())
422423
}
423424

425+
/// Get the color table for this band if it has one.
426+
pub fn color_table(&self) -> Option<ColorTable> {
427+
let c_color_table = unsafe { gdal_sys::GDALGetRasterColorTable(self.c_rasterband) };
428+
if c_color_table.is_null() {
429+
return None;
430+
}
431+
Some(ColorTable::from_c_color_table(c_color_table))
432+
}
433+
424434
/// Returns the scale of this band if set.
425435
pub fn scale(&self) -> Option<f64> {
426436
let mut pb_success = 1;
@@ -612,3 +622,144 @@ impl ColorInterpretation {
612622
_string(rv)
613623
}
614624
}
625+
626+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
627+
pub enum PaletteInterpretation {
628+
Gray,
629+
Rgba,
630+
Cmyk,
631+
Hls,
632+
}
633+
634+
impl PaletteInterpretation {
635+
fn from_c_int(palette_interpretation: GDALPaletteInterp::Type) -> Self {
636+
match palette_interpretation {
637+
GDALPaletteInterp::GPI_Gray => Self::Gray,
638+
GDALPaletteInterp::GPI_RGB => Self::Rgba,
639+
GDALPaletteInterp::GPI_CMYK => Self::Cmyk,
640+
GDALPaletteInterp::GPI_HLS => Self::Hls,
641+
_ => unreachable!("GDAL has implemented a new type of `GDALPaletteInterp`"),
642+
}
643+
}
644+
}
645+
646+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
647+
pub struct GrayEntry {
648+
pub g: i16,
649+
}
650+
651+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
652+
pub struct RgbaEntry {
653+
pub r: i16,
654+
pub g: i16,
655+
pub b: i16,
656+
pub a: i16,
657+
}
658+
659+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
660+
pub struct CmykEntry {
661+
pub c: i16,
662+
pub m: i16,
663+
pub y: i16,
664+
pub k: i16,
665+
}
666+
667+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
668+
pub struct HlsEntry {
669+
pub h: i16,
670+
pub l: i16,
671+
pub s: i16,
672+
}
673+
674+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
675+
pub enum ColorEntry {
676+
Gray(GrayEntry),
677+
Rgba(RgbaEntry),
678+
Cmyk(CmykEntry),
679+
Hls(HlsEntry),
680+
}
681+
682+
/// Color table for raster bands that use the PaletteIndex color interpretation.
683+
///
684+
/// This object carries the lifetime of the raster band that
685+
/// contains it. This is necessary to prevent the raster band
686+
/// from being dropped before the color table.
687+
pub struct ColorTable<'a> {
688+
palette_interpretation: PaletteInterpretation,
689+
c_color_table: GDALColorTableH,
690+
phantom_raster_band: PhantomData<&'a RasterBand<'a>>,
691+
}
692+
693+
impl<'a> ColorTable<'a> {
694+
fn from_c_color_table(c_color_table: GDALColorTableH) -> Self {
695+
let interp_index = unsafe { gdal_sys::GDALGetPaletteInterpretation(c_color_table) };
696+
ColorTable {
697+
palette_interpretation: PaletteInterpretation::from_c_int(interp_index),
698+
c_color_table,
699+
phantom_raster_band: PhantomData,
700+
}
701+
}
702+
703+
/// How the values of this color table are interpreted.
704+
pub fn palette_interpretation(&self) -> PaletteInterpretation {
705+
self.palette_interpretation
706+
}
707+
708+
/// Get the number of color entries in this color table.
709+
pub fn entry_count(&self) -> usize {
710+
unsafe { gdal_sys::GDALGetColorEntryCount(self.c_color_table) as usize }
711+
}
712+
713+
/// Get a color entry.
714+
pub fn entry(&self, index: usize) -> Option<ColorEntry> {
715+
let color_entry = unsafe {
716+
let c_color_entry = gdal_sys::GDALGetColorEntry(self.c_color_table, index as i32);
717+
if c_color_entry.is_null() {
718+
return None;
719+
}
720+
*c_color_entry
721+
};
722+
match self.palette_interpretation {
723+
PaletteInterpretation::Gray => Some(ColorEntry::Gray(GrayEntry { g: color_entry.c1 })),
724+
PaletteInterpretation::Rgba => Some(ColorEntry::Rgba(RgbaEntry {
725+
r: color_entry.c1,
726+
g: color_entry.c2,
727+
b: color_entry.c3,
728+
a: color_entry.c4,
729+
})),
730+
PaletteInterpretation::Cmyk => Some(ColorEntry::Cmyk(CmykEntry {
731+
c: color_entry.c1,
732+
m: color_entry.c2,
733+
y: color_entry.c3,
734+
k: color_entry.c4,
735+
})),
736+
PaletteInterpretation::Hls => Some(ColorEntry::Hls(HlsEntry {
737+
h: color_entry.c1,
738+
l: color_entry.c2,
739+
s: color_entry.c3,
740+
})),
741+
}
742+
}
743+
744+
/// Get a color entry as RGB.
745+
pub fn entry_as_rgb(&self, index: usize) -> Option<RgbaEntry> {
746+
let mut color_entry = GDALColorEntry {
747+
c1: 0,
748+
c2: 0,
749+
c3: 0,
750+
c4: 0,
751+
};
752+
if unsafe {
753+
gdal_sys::GDALGetColorEntryAsRGB(self.c_color_table, index as i32, &mut color_entry)
754+
} == 0
755+
{
756+
return None;
757+
}
758+
Some(RgbaEntry {
759+
r: color_entry.c1,
760+
g: color_entry.c2,
761+
b: color_entry.c3,
762+
a: color_entry.c4,
763+
})
764+
}
765+
}

src/raster/tests.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -706,3 +706,43 @@ fn test_rasterband_unit() {
706706

707707
assert_eq!(rasterband.unit(), "m".to_string());
708708
}
709+
710+
#[test]
711+
fn test_color_table() {
712+
use crate::raster::rasterband::{ColorEntry, PaletteInterpretation};
713+
714+
// Raster containing one band.
715+
let dataset = Dataset::open(fixture!("test_color_table.tif")).expect("open failure");
716+
assert_eq!(dataset.raster_count(), 1);
717+
718+
// Band is PaletteIndex.
719+
let band = dataset.rasterband(1).expect("rasterband failure");
720+
assert_eq!(
721+
band.color_interpretation(),
722+
ColorInterpretation::PaletteIndex
723+
);
724+
725+
// Color table is RGB.
726+
let color_table = band.color_table().unwrap();
727+
assert_eq!(
728+
color_table.palette_interpretation(),
729+
PaletteInterpretation::Rgba
730+
);
731+
732+
// Color table has 256 entries.
733+
let entry_count = color_table.entry_count();
734+
assert_eq!(entry_count, 256);
735+
736+
// Check that entry and entry_as_rgb are the same.
737+
for index in 0..entry_count {
738+
if let ColorEntry::Rgba(entry) = color_table.entry(index).unwrap() {
739+
let rgb_entry = color_table.entry_as_rgb(index).unwrap();
740+
assert_eq!(entry.r, rgb_entry.r);
741+
assert_eq!(entry.g, rgb_entry.g);
742+
assert_eq!(entry.b, rgb_entry.b);
743+
assert_eq!(entry.a, rgb_entry.a);
744+
} else {
745+
panic!();
746+
}
747+
}
748+
}

0 commit comments

Comments
 (0)