gistools/readers/geotiff/
color.rs1use super::{
2 Raster,
3 constants::{ExtraSamplesValues, PhotometricInterpretations},
4};
5use alloc::{vec, vec::Vec};
6use libm::{fmax, fmin, pow};
7
8pub fn build_samples(
18 pi: PhotometricInterpretations,
19 bits_per_sample: Option<Vec<u16>>,
20 extra_samples: Option<ExtraSamplesValues>,
21) -> Vec<u16> {
22 let extra_samples = extra_samples.unwrap_or(ExtraSamplesValues::Unspecified);
23 let bits_per_sample = bits_per_sample.unwrap_or(vec![0]);
24 let mut samples;
25 if pi == PhotometricInterpretations::RGB {
26 samples = vec![0, 1, 2, 3];
27 if extra_samples != ExtraSamplesValues::Unspecified {
29 samples = vec![];
30 for i in 0..bits_per_sample.len() {
31 samples.push(i as u16);
32 }
33 }
34 } else {
35 match pi {
36 PhotometricInterpretations::WhiteIsZero
37 | PhotometricInterpretations::BlackIsZero
38 | PhotometricInterpretations::Palette => {
39 samples = vec![0];
40 }
41 PhotometricInterpretations::CMYK => {
42 samples = vec![0, 1, 2, 3];
43 }
44 PhotometricInterpretations::YCbCr
45 | PhotometricInterpretations::CIELab
46 | PhotometricInterpretations::ICCLab => {
47 samples = vec![0, 1, 2];
48 }
49 _ => panic!("Invalid or unsupported photometric interpretation."),
50 }
51 }
52
53 samples
54}
55
56pub fn convert_color_space(
65 pi: PhotometricInterpretations,
66 raster: &mut Raster,
67 max: f64,
68 color_map: Option<Vec<u16>>,
69) {
70 if pi == PhotometricInterpretations::RGB {
71 } else if pi == PhotometricInterpretations::WhiteIsZero {
72 from_white_is_zero(raster, max);
73 } else if pi == PhotometricInterpretations::BlackIsZero {
74 from_black_is_zero(raster, max);
75 } else if pi == PhotometricInterpretations::Palette {
76 from_palette(raster, color_map);
77 } else if pi == PhotometricInterpretations::CMYK {
78 from_cmyk(raster);
79 } else if pi == PhotometricInterpretations::YCbCr {
80 from_ycb_cr(raster);
81 } else if pi == PhotometricInterpretations::CIELab {
82 from_cei_lab(raster);
83 } else {
84 panic!("Unsupported photometric interpretation {:?}.", pi);
85 }
86}
87
88pub fn from_white_is_zero(raster: &mut Raster, max: f64) {
94 let mut rbgdata = vec![0_f64; raster.width * raster.height * 3];
95 let data = &raster.data;
96 let mut i = 0;
97 let mut j = 0;
98 while i < data.len() {
99 let value = 256. - (data[i] / max) * 256.;
100 rbgdata[j] = value;
101 rbgdata[j + 1] = value;
102 rbgdata[j + 2] = value;
103
104 i += 1;
105 j += 3;
106 }
107 raster.data = rbgdata;
108}
109
110pub fn from_black_is_zero(raster: &mut Raster, max: f64) {
116 let mut rbgdata = vec![0_f64; raster.width * raster.height * 3];
117 let data = &raster.data;
118 let mut i = 0;
119 let mut j = 0;
120 while i < data.len() {
121 let value = (data[i] / max) * 256.;
122 rbgdata[j] = value;
123 rbgdata[j + 1] = value;
124 rbgdata[j + 2] = value;
125
126 i += 1;
127 j += 3;
128 }
129 raster.data = rbgdata;
130}
131
132pub fn from_palette(raster: &mut Raster, color_map: Option<Vec<u16>>) {
138 let color_map = color_map.unwrap_or_default();
139 let mut rbgdata = vec![0_f64; raster.width * raster.height * 3];
140 let data = &raster.data;
141 let green_offset = color_map.len() / 3;
142 let blue_offset = (color_map.len() / 3) * 2;
143 let mut i = 0;
144 let mut j = 0;
145 while i < data.len() {
146 let map_index = data[i] as usize;
147 rbgdata[j] = (color_map[map_index] as f64 / 65_536.) * 256.;
148 rbgdata[j + 1] = (color_map[map_index + green_offset] as f64 / 65_536.) * 256.;
149 rbgdata[j + 2] = (color_map[map_index + blue_offset] as f64 / 65_536.) * 256.;
150
151 i += 1;
152 j += 3;
153 }
154 raster.data = rbgdata;
155}
156
157pub fn from_cmyk(raster: &mut Raster) {
162 let mut rbgdata = vec![0_f64; raster.width * raster.height * 3];
163 let data = &raster.data;
164 let mut i = 0;
165 let mut j = 0;
166 while i < data.len() {
167 let c = data[i] / 255.0;
168 let m = data[i + 1] / 255.0;
169 let y = data[i + 2] / 255.0;
170 let k = data[i + 3] / 255.0;
171
172 rbgdata[j] = 255.0 * (1.0 - c) * (1.0 - k);
173 rbgdata[j + 1] = 255.0 * (1.0 - m) * (1.0 - k);
174 rbgdata[j + 2] = 255.0 * (1.0 - y) * (1.0 - k);
175
176 i += 4;
177 j += 3;
178 }
179 raster.data = rbgdata;
180}
181
182pub fn from_ycb_cr(raster: &mut Raster) {
187 let mut rbgdata = vec![0_f64; raster.width * raster.height * 3];
188 let data = &raster.data;
189 let mut i = 0;
190 let mut j = 0;
191 while i < data.len() {
192 let y = data[i];
193 let cb = data[i + 1] - (0x80 as f64);
194 let cr = data[i + 2] - (0x80 as f64);
195
196 rbgdata[j] = y + 1.402 * cr;
197 rbgdata[j + 1] = y - 0.34414 * cb - 0.71414 * cr;
198 rbgdata[j + 2] = y + 1.772 * cb;
199 i += 3;
200 j += 3;
201 }
202 raster.data = rbgdata;
203}
204
205const XN: f64 = 0.95047;
206const YN: f64 = 1.0;
207const ZN: f64 = 1.08883;
208
209pub fn from_cei_lab(raster: &mut Raster) {
215 let mut rbgdata = vec![0_f64; raster.width * raster.height * 3];
216 let data = &raster.data;
217
218 let mut l: f64;
219 let mut a_: f64;
220 let mut b_: f64;
221 let mut x: f64;
222 let mut y: f64;
223 let mut z: f64;
224 let mut r: f64;
225 let mut g: f64;
226 let mut b: f64;
227 let mut i = 0;
228 let mut j = 0;
229 while i < data.len() {
230 l = data[i];
231 a_ = (data[i + 1] as i8) as f64; b_ = (data[i + 2] as i8) as f64; y = (l + 16.) / 116.;
234 x = a_ / 500. + y;
235 z = y - b_ / 200.;
236
237 x = XN * if x * x * x > 0.008856 { x * x * x } else { (x - 16. / 116.) / 7.787 };
238 y = YN * if y * y * y > 0.008856 { y * y * y } else { (y - 16. / 116.) / 7.787 };
239 z = ZN * if z * z * z > 0.008856 { z * z * z } else { (z - 16. / 116.) / 7.787 };
240
241 r = x * 3.2406 + y * -1.5372 + z * -0.4986;
242 g = x * -0.9689 + y * 1.8758 + z * 0.0415;
243 b = x * 0.0557 + y * -0.204 + z * 1.057;
244
245 r = if r > 0.0031308 { 1.055 * pow(r, (1. / 2.4) - 0.055) } else { 12.92 * r };
246 g = if g > 0.0031308 { 1.055 * pow(g, (1. / 2.4) - 0.055) } else { 12.92 * g };
247 b = if b > 0.0031308 { 1.055 * pow(b, (1. / 2.4) - 0.055) } else { 12.92 * b };
248
249 rbgdata[j] = fmax(0., fmin(1., r)) * 255.;
250 rbgdata[j + 1] = fmax(0., fmin(1., g)) * 255.;
251 rbgdata[j + 2] = fmax(0., fmin(1., b)) * 255.;
252 i += 3;
253 j += 3;
254 }
255 raster.data = rbgdata;
256}