1use zenpixels::{ColorPrimaries, TransferFunction};
8
9use crate::convert::{hlg_eotf, hlg_oetf, pq_eotf, pq_oetf};
10use crate::gamut::GamutMatrix;
11
12pub trait TransferFunctionExt {
18 #[must_use]
22 fn linearize(&self, v: f32) -> f32;
23
24 #[must_use]
28 fn delinearize(&self, v: f32) -> f32;
29}
30
31impl TransferFunctionExt for TransferFunction {
32 #[allow(unreachable_patterns)]
33 fn linearize(&self, v: f32) -> f32 {
34 match self {
35 Self::Linear | Self::Unknown => v,
36 Self::Srgb => linear_srgb::precise::srgb_to_linear(v),
37 Self::Bt709 => linear_srgb::tf::bt709_to_linear(v),
38 Self::Pq => pq_eotf(v),
39 Self::Hlg => hlg_eotf(v),
40 _ => v,
41 }
42 }
43
44 #[allow(unreachable_patterns)]
45 fn delinearize(&self, v: f32) -> f32 {
46 match self {
47 Self::Linear | Self::Unknown => v,
48 Self::Srgb => linear_srgb::precise::linear_to_srgb(v),
49 Self::Bt709 => linear_srgb::tf::linear_to_bt709(v),
50 Self::Pq => pq_oetf(v),
51 Self::Hlg => hlg_oetf(v),
52 _ => v,
53 }
54 }
55}
56
57#[allow(clippy::wrong_self_convention)]
63pub trait ColorPrimariesExt {
64 fn to_xyz_matrix(&self) -> Option<&'static GamutMatrix>;
68
69 fn from_xyz_matrix(&self) -> Option<&'static GamutMatrix>;
73}
74
75impl ColorPrimariesExt for ColorPrimaries {
76 #[allow(unreachable_patterns)]
77 fn to_xyz_matrix(&self) -> Option<&'static GamutMatrix> {
78 match self {
79 Self::Bt709 => Some(&crate::gamut::BT709_TO_XYZ),
80 Self::DisplayP3 => Some(&crate::gamut::DISPLAY_P3_TO_XYZ),
81 Self::Bt2020 => Some(&crate::gamut::BT2020_TO_XYZ),
82 _ => None,
83 }
84 }
85
86 #[allow(unreachable_patterns)]
87 fn from_xyz_matrix(&self) -> Option<&'static GamutMatrix> {
88 match self {
89 Self::Bt709 => Some(&crate::gamut::XYZ_TO_BT709),
90 Self::DisplayP3 => Some(&crate::gamut::XYZ_TO_DISPLAY_P3),
91 Self::Bt2020 => Some(&crate::gamut::XYZ_TO_BT2020),
92 _ => None,
93 }
94 }
95}
96
97use alloc::sync::Arc;
102use whereat::{At, ResultAtExt};
103use zenpixels::PixelDescriptor;
104use zenpixels::buffer::PixelBuffer;
105use zenpixels::descriptor::{AlphaMode, ChannelLayout, ChannelType};
106
107pub trait PixelBufferConvertExt {
109 fn convert_to(&self, target: PixelDescriptor) -> Result<PixelBuffer, At<crate::ConvertError>>;
116
117 fn try_add_alpha(&self) -> Result<PixelBuffer, At<crate::ConvertError>>;
123
124 fn try_widen_to_u16(&self) -> Result<PixelBuffer, At<crate::ConvertError>>;
126
127 fn try_narrow_to_u8(&self) -> Result<PixelBuffer, At<crate::ConvertError>>;
129
130 fn linearize(&self) -> Result<PixelBuffer, At<crate::ConvertError>>;
137
138 fn delinearize(
147 &self,
148 transfer: TransferFunction,
149 ) -> Result<PixelBuffer, At<crate::ConvertError>>;
150}
151
152#[cfg(feature = "rgb")]
156pub trait PixelBufferConvertTypedExt: PixelBufferConvertExt {
157 fn to_rgb8(&self) -> PixelBuffer<rgb::Rgb<u8>>;
159
160 fn to_rgba8(&self) -> PixelBuffer<rgb::Rgba<u8>>;
162
163 fn to_gray8(&self) -> PixelBuffer<rgb::Gray<u8>>;
165
166 fn to_bgra8(&self) -> PixelBuffer<rgb::alt::BGRA<u8>>;
168}
169
170fn assert_not_cmyk(desc: &PixelDescriptor) {
172 assert!(
173 desc.color_model() != crate::ColorModel::Cmyk,
174 "CMYK pixel data cannot be processed by zenpixels-convert. \
175 Use a CMS (e.g., moxcms) with an ICC profile for CMYK↔RGB conversion."
176 );
177}
178
179impl PixelBufferConvertExt for PixelBuffer {
180 #[track_caller]
181 fn convert_to(&self, target: PixelDescriptor) -> Result<PixelBuffer, At<crate::ConvertError>> {
182 let src_desc = self.descriptor();
183 assert_not_cmyk(&src_desc);
184 assert_not_cmyk(&target);
185 if src_desc == target {
186 let dst_stride = target.aligned_stride(self.width());
188 let total = dst_stride
189 .checked_mul(self.height() as usize)
190 .ok_or_else(|| whereat::at!(crate::ConvertError::AllocationFailed))?;
191 let mut out = alloc::vec![0u8; total];
192 let src_slice = self.as_slice();
193 for y in 0..self.height() {
194 let src_row = src_slice.row(y);
195 let dst_start = y as usize * dst_stride;
196 out[dst_start..dst_start + src_row.len()].copy_from_slice(src_row);
197 }
198 let mut buf = PixelBuffer::from_vec(out, self.width(), self.height(), target)
199 .map_err(|_| whereat::at!(crate::ConvertError::AllocationFailed))?;
200 if let Some(ctx) = self.color_context() {
201 buf = buf.with_color_context(Arc::clone(ctx));
202 }
203 return Ok(buf);
204 }
205
206 let mut converter = crate::RowConverter::new(src_desc, target).at()?;
207
208 let dst_stride = target.aligned_stride(self.width());
209 let total = dst_stride
210 .checked_mul(self.height() as usize)
211 .ok_or_else(|| whereat::at!(crate::ConvertError::AllocationFailed))?;
212 let mut out = alloc::vec![0u8; total];
213
214 let src_slice = self.as_slice();
215 for y in 0..self.height() {
216 let src_row = src_slice.row(y);
217 let dst_start = y as usize * dst_stride;
218 let dst_end = dst_start + dst_stride;
219 converter.convert_row(src_row, &mut out[dst_start..dst_end], self.width());
220 }
221
222 let mut buf = PixelBuffer::from_vec(out, self.width(), self.height(), target)
223 .map_err(|_| whereat::at!(crate::ConvertError::AllocationFailed))?;
224 if let Some(ctx) = self.color_context() {
225 buf = buf.with_color_context(Arc::clone(ctx));
226 }
227 Ok(buf)
228 }
229
230 #[track_caller]
231 fn try_add_alpha(&self) -> Result<PixelBuffer, At<crate::ConvertError>> {
232 let desc = self.descriptor();
233 let target_layout = match desc.layout() {
234 ChannelLayout::Gray => ChannelLayout::GrayAlpha,
235 ChannelLayout::Rgb => ChannelLayout::Rgba,
236 other => other,
237 };
238 let alpha = if target_layout.has_alpha() && desc.alpha().is_none() {
239 Some(AlphaMode::Straight)
240 } else {
241 desc.alpha()
242 };
243 let target =
244 PixelDescriptor::new(desc.channel_type(), target_layout, alpha, desc.transfer());
245 self.convert_to(target)
246 }
247
248 #[track_caller]
249 fn try_widen_to_u16(&self) -> Result<PixelBuffer, At<crate::ConvertError>> {
250 let desc = self.descriptor();
251 let target = PixelDescriptor::new(
252 ChannelType::U16,
253 desc.layout(),
254 desc.alpha(),
255 desc.transfer(),
256 );
257 self.convert_to(target)
258 }
259
260 #[track_caller]
261 fn try_narrow_to_u8(&self) -> Result<PixelBuffer, At<crate::ConvertError>> {
262 let desc = self.descriptor();
263 let target = PixelDescriptor::new(
264 ChannelType::U8,
265 desc.layout(),
266 desc.alpha(),
267 desc.transfer(),
268 );
269 self.convert_to(target)
270 }
271
272 #[track_caller]
273 fn linearize(&self) -> Result<PixelBuffer, At<crate::ConvertError>> {
274 let desc = self.descriptor();
275 let target = PixelDescriptor::new_full(
276 ChannelType::F32,
277 desc.layout(),
278 desc.alpha(),
279 TransferFunction::Linear,
280 desc.primaries,
281 );
282 self.convert_to(target)
283 }
284
285 #[track_caller]
286 fn delinearize(
287 &self,
288 transfer: TransferFunction,
289 ) -> Result<PixelBuffer, At<crate::ConvertError>> {
290 let target = self.descriptor().with_transfer(transfer);
291 self.convert_to(target)
292 }
293}
294
295#[cfg(feature = "rgb")]
296use zenpixels::buffer::Pixel;
297
298#[cfg(feature = "rgb")]
299impl PixelBufferConvertTypedExt for PixelBuffer {
300 fn to_rgb8(&self) -> PixelBuffer<rgb::Rgb<u8>> {
301 convert_to_typed(self, PixelDescriptor::RGB8_SRGB)
302 }
303
304 fn to_rgba8(&self) -> PixelBuffer<rgb::Rgba<u8>> {
305 convert_to_typed(self, PixelDescriptor::RGBA8_SRGB)
306 }
307
308 fn to_gray8(&self) -> PixelBuffer<rgb::Gray<u8>> {
309 convert_to_typed(self, PixelDescriptor::GRAY8_SRGB)
310 }
311
312 fn to_bgra8(&self) -> PixelBuffer<rgb::alt::BGRA<u8>> {
313 convert_to_typed(self, PixelDescriptor::BGRA8_SRGB)
314 }
315}
316
317#[cfg(feature = "rgb")]
319fn convert_to_typed<Q: Pixel>(buf: &PixelBuffer, target: PixelDescriptor) -> PixelBuffer<Q> {
320 use alloc::vec;
321 let mut conv = crate::RowConverter::new(buf.descriptor(), target)
322 .expect("RowConverter: no conversion path");
323 let dst_bpp = target.bytes_per_pixel();
324 let dst_stride = target.aligned_stride(buf.width());
325 let total = dst_stride * buf.height() as usize;
326 let mut out = vec![0u8; total];
327 let src_slice = buf.as_slice();
328 for y in 0..buf.height() {
329 let src_row = src_slice.row(y);
330 let dst_start = y as usize * dst_stride;
331 let dst_end = dst_start + buf.width() as usize * dst_bpp;
332 conv.convert_row(src_row, &mut out[dst_start..dst_end], buf.width());
333 }
334 let erased = PixelBuffer::from_vec(out, buf.width(), buf.height(), target)
337 .expect("convert_to_typed: buffer construction failed");
338 let erased = if let Some(ctx) = buf.color_context() {
340 erased.with_color_context(Arc::clone(ctx))
341 } else {
342 erased
343 };
344 erased
345 .try_typed::<Q>()
346 .expect("convert_to_typed: type mismatch after conversion")
347}
348
349#[cfg(test)]
350mod tests {
351 use super::*;
352
353 #[test]
356 #[should_panic(expected = "CMYK pixel data cannot be processed")]
357 fn cmyk_rejected_by_convert_to() {
358 let cmyk_data = vec![0u8; 4 * 4]; let buf = PixelBuffer::from_vec(cmyk_data, 2, 2, PixelDescriptor::CMYK8).unwrap();
360 let _ = buf.convert_to(PixelDescriptor::RGB8_SRGB);
361 }
362
363 #[test]
364 #[should_panic(expected = "CMYK pixel data cannot be processed")]
365 fn cmyk_rejected_as_convert_target() {
366 let rgb_data = vec![0u8; 3 * 4]; let buf = PixelBuffer::from_vec(rgb_data, 2, 2, PixelDescriptor::RGB8_SRGB).unwrap();
368 let _ = buf.convert_to(PixelDescriptor::CMYK8);
369 }
370
371 #[test]
374 fn srgb_linearize_roundtrip() {
375 let tf = TransferFunction::Srgb;
376 for &v in &[0.0, 0.04045, 0.1, 0.5, 0.73, 1.0] {
377 let lin = tf.linearize(v);
378 let back = tf.delinearize(lin);
379 assert!(
380 (v - back).abs() < 1e-5,
381 "sRGB roundtrip failed for {v}: linearize={lin}, delinearize={back}"
382 );
383 }
384 }
385
386 #[test]
387 fn pq_linearize_roundtrip() {
388 let tf = TransferFunction::Pq;
389 for &v in &[0.0, 0.1, 0.5, 0.75, 1.0] {
392 let lin = tf.linearize(v);
393 let back = tf.delinearize(lin);
394 assert!(
395 (v - back).abs() < 5e-4,
396 "PQ roundtrip failed for {v}: linearize={lin}, delinearize={back}"
397 );
398 }
399 }
400
401 #[test]
402 fn hlg_linearize_roundtrip() {
403 let tf = TransferFunction::Hlg;
404 for &v in &[0.0, 0.1, 0.3, 0.5, 0.8, 1.0] {
405 let lin = tf.linearize(v);
406 let back = tf.delinearize(lin);
407 assert!(
408 (v - back).abs() < 1e-4,
409 "HLG roundtrip failed for {v}: linearize={lin}, delinearize={back}"
410 );
411 }
412 }
413
414 #[test]
415 fn linear_identity() {
416 let tf = TransferFunction::Linear;
417 for &v in &[0.0, 0.5, 1.0] {
418 assert_eq!(tf.linearize(v), v);
419 assert_eq!(tf.delinearize(v), v);
420 }
421 }
422
423 #[test]
426 fn xyz_matrix_availability() {
427 assert!(ColorPrimaries::Bt709.to_xyz_matrix().is_some());
428 assert!(ColorPrimaries::Bt709.from_xyz_matrix().is_some());
429 assert!(ColorPrimaries::DisplayP3.to_xyz_matrix().is_some());
430 assert!(ColorPrimaries::Bt2020.to_xyz_matrix().is_some());
431 assert!(ColorPrimaries::Unknown.to_xyz_matrix().is_none());
432 assert!(ColorPrimaries::Unknown.from_xyz_matrix().is_none());
433 }
434
435 #[test]
436 fn xyz_roundtrip_bt709() {
437 let to = ColorPrimaries::Bt709.to_xyz_matrix().unwrap();
438 let from = ColorPrimaries::Bt709.from_xyz_matrix().unwrap();
439 let rgb = [0.5f32, 0.3, 0.8];
440 let mut v = rgb;
441 crate::gamut::apply_matrix_f32(&mut v, to);
442 crate::gamut::apply_matrix_f32(&mut v, from);
443 for c in 0..3 {
444 assert!(
445 (v[c] - rgb[c]).abs() < 1e-4,
446 "XYZ roundtrip BT.709 ch{c}: {:.6} vs {:.6}",
447 v[c],
448 rgb[c]
449 );
450 }
451 }
452
453 #[test]
456 fn bt709_linearize_roundtrip() {
457 let tf = TransferFunction::Bt709;
458 for &v in &[0.0, 0.04045, 0.1, 0.5, 0.73, 1.0] {
459 let lin = tf.linearize(v);
460 let back = tf.delinearize(lin);
461 assert!(
462 (v - back).abs() < 1e-5,
463 "BT.709 roundtrip failed for {v}: linearize={lin}, delinearize={back}"
464 );
465 }
466 }
467
468 #[test]
469 fn unknown_transfer_identity() {
470 let tf = TransferFunction::Unknown;
471 for &v in &[0.0, 0.25, 0.5, 0.75, 1.0] {
472 assert_eq!(
473 tf.linearize(v),
474 v,
475 "Unknown linearize should be identity for {v}"
476 );
477 assert_eq!(
478 tf.delinearize(v),
479 v,
480 "Unknown delinearize should be identity for {v}"
481 );
482 }
483 }
484
485 use super::PixelBufferConvertExt;
488
489 #[test]
490 fn convert_to_identity() {
491 let data = vec![100u8, 150, 200, 50, 100, 150];
492 let buf = PixelBuffer::from_vec(data.clone(), 2, 1, PixelDescriptor::RGB8_SRGB).unwrap();
493 let out = buf.convert_to(PixelDescriptor::RGB8_SRGB).unwrap();
494 assert_eq!(out.descriptor(), PixelDescriptor::RGB8_SRGB);
495 assert_eq!(out.width(), 2);
496 assert_eq!(out.height(), 1);
497 assert_eq!(&out.as_slice().row(0)[..6], &data[..]);
498 }
499
500 #[test]
501 fn convert_to_rgba8() {
502 let data = vec![100u8, 150, 200, 50, 100, 150];
503 let buf = PixelBuffer::from_vec(data, 2, 1, PixelDescriptor::RGB8_SRGB).unwrap();
504 let out = buf.convert_to(PixelDescriptor::RGBA8_SRGB).unwrap();
505 assert_eq!(out.descriptor(), PixelDescriptor::RGBA8_SRGB);
506 let slice = out.as_slice();
507 let row = slice.row(0);
508 assert_eq!(row[0], 100);
510 assert_eq!(row[1], 150);
511 assert_eq!(row[2], 200);
512 assert_eq!(row[3], 255);
513 assert_eq!(row[4], 50);
515 assert_eq!(row[5], 100);
516 assert_eq!(row[6], 150);
517 assert_eq!(row[7], 255);
518 }
519
520 #[test]
521 fn try_add_alpha_rgb() {
522 let data = vec![100u8, 150, 200, 50, 100, 150];
523 let buf = PixelBuffer::from_vec(data, 2, 1, PixelDescriptor::RGB8_SRGB).unwrap();
524 let out = buf.try_add_alpha().unwrap();
525 assert_eq!(
527 out.descriptor().layout(),
528 zenpixels::descriptor::ChannelLayout::Rgba
529 );
530 let slice = out.as_slice();
531 let row = slice.row(0);
532 assert_eq!(row[3], 255);
533 assert_eq!(row[7], 255);
534 }
535
536 #[test]
537 fn try_widen_to_u16() {
538 let data = vec![100u8, 150, 200, 50, 100, 150];
539 let buf = PixelBuffer::from_vec(data, 2, 1, PixelDescriptor::RGB8_SRGB).unwrap();
540 let out = buf.try_widen_to_u16().unwrap();
541 assert_eq!(
542 out.descriptor().channel_type(),
543 zenpixels::descriptor::ChannelType::U16
544 );
545 let slice = out.as_slice();
546 let row = slice.row(0);
547 for (i, &expected_u8) in [100u8, 150, 200, 50, 100, 150].iter().enumerate() {
549 let lo = row[i * 2];
550 let hi = row[i * 2 + 1];
551 let val = u16::from_le_bytes([lo, hi]);
552 let expected = expected_u8 as u16 * 257;
553 assert_eq!(
554 val, expected,
555 "channel {i}: expected {expected} (u8={expected_u8}*257), got {val}"
556 );
557 }
558 }
559
560 #[test]
561 fn linearize_srgb_to_linear_f32() {
562 let data = vec![128u8, 128, 128, 64, 64, 64];
563 let buf = PixelBuffer::from_vec(data, 2, 1, PixelDescriptor::RGB8_SRGB).unwrap();
564 let lin = buf.linearize().unwrap();
565 assert_eq!(lin.descriptor().transfer(), TransferFunction::Linear);
566 assert_eq!(
567 lin.descriptor().channel_type(),
568 zenpixels::descriptor::ChannelType::F32
569 );
570 assert_eq!(lin.descriptor().primaries, ColorPrimaries::Bt709);
571 let slice = lin.as_slice();
573 let row = slice.row(0);
574 let r = f32::from_le_bytes([row[0], row[1], row[2], row[3]]);
575 assert!(
576 (r - 0.216).abs() < 0.01,
577 "sRGB 128 should linearize to ~0.216, got {r}"
578 );
579 }
580
581 #[test]
582 fn delinearize_linear_to_srgb() {
583 let linear_val: f32 = 0.216;
585 let mut data = vec![0u8; 24]; for i in 0..6 {
587 let bytes = linear_val.to_le_bytes();
588 data[i * 4..i * 4 + 4].copy_from_slice(&bytes);
589 }
590 let buf = PixelBuffer::from_vec(data, 2, 1, PixelDescriptor::RGBF32_LINEAR).unwrap();
591 let srgb = buf.delinearize(TransferFunction::Srgb).unwrap();
592 assert_eq!(srgb.descriptor().transfer(), TransferFunction::Srgb);
593 let slice = srgb.as_slice();
595 let row = slice.row(0);
596 let r = f32::from_le_bytes([row[0], row[1], row[2], row[3]]);
597 assert!(
598 (r - 0.502).abs() < 0.01,
599 "linear 0.216 should delinearize to ~0.502, got {r}"
600 );
601 }
602
603 #[test]
604 fn linearize_delinearize_roundtrip() {
605 let data = vec![100u8, 150, 200, 50, 100, 150];
606 let buf = PixelBuffer::from_vec(data.clone(), 2, 1, PixelDescriptor::RGB8_SRGB).unwrap();
607 let lin = buf.linearize().unwrap();
608 let back = lin.delinearize(TransferFunction::Srgb).unwrap();
610 let slice = back.as_slice();
612 let row = slice.row(0);
613 let r = f32::from_le_bytes([row[0], row[1], row[2], row[3]]);
614 let expected = 100.0 / 255.0;
615 assert!(
616 (r - expected).abs() < 0.005,
617 "roundtrip pixel 0 R: expected ~{expected}, got {r}"
618 );
619 }
620
621 #[test]
622 fn linearize_preserves_alpha() {
623 let data = vec![100u8, 150, 200, 128, 50, 100, 150, 64];
624 let buf = PixelBuffer::from_vec(data, 2, 1, PixelDescriptor::RGBA8_SRGB).unwrap();
625 let lin = buf.linearize().unwrap();
626 assert_eq!(
627 lin.descriptor().layout(),
628 zenpixels::descriptor::ChannelLayout::Rgba
629 );
630 assert!(lin.descriptor().alpha().is_some());
631 }
632
633 #[test]
634 fn linearize_preserves_primaries() {
635 let data = vec![100u8, 150, 200, 50, 100, 150];
636 let desc = PixelDescriptor::RGB8_SRGB.with_primaries(ColorPrimaries::DisplayP3);
637 let buf = PixelBuffer::from_vec(data, 2, 1, desc).unwrap();
638 let lin = buf.linearize().unwrap();
639 assert_eq!(lin.descriptor().primaries, ColorPrimaries::DisplayP3);
640 }
641
642 #[test]
643 fn linearize_already_linear_is_identity() {
644 let val: f32 = 0.5;
645 let mut data = vec![0u8; 12]; for i in 0..3 {
647 data[i * 4..i * 4 + 4].copy_from_slice(&val.to_le_bytes());
648 }
649 let buf = PixelBuffer::from_vec(data, 1, 1, PixelDescriptor::RGBF32_LINEAR).unwrap();
650 let lin = buf.linearize().unwrap();
651 let slice = lin.as_slice();
652 let row = slice.row(0);
653 let r = f32::from_le_bytes([row[0], row[1], row[2], row[3]]);
654 assert!(
655 (r - val).abs() < 1e-6,
656 "already-linear should be identity, got {r}"
657 );
658 }
659
660 #[test]
661 fn try_narrow_to_u8() {
662 let values: [u16; 6] = [
664 100 * 257,
665 150 * 257,
666 200 * 257,
667 50 * 257,
668 100 * 257,
669 150 * 257,
670 ];
671 let mut data = vec![0u8; 12];
672 for (i, &v) in values.iter().enumerate() {
673 let bytes = v.to_le_bytes();
674 data[i * 2] = bytes[0];
675 data[i * 2 + 1] = bytes[1];
676 }
677 let buf = PixelBuffer::from_vec(data, 2, 1, PixelDescriptor::RGB16_SRGB).unwrap();
678 let out = buf.try_narrow_to_u8().unwrap();
679 assert_eq!(
680 out.descriptor().channel_type(),
681 zenpixels::descriptor::ChannelType::U8
682 );
683 let slice = out.as_slice();
684 let row = slice.row(0);
685 assert_eq!(row[0], 100);
686 assert_eq!(row[1], 150);
687 assert_eq!(row[2], 200);
688 assert_eq!(row[3], 50);
689 assert_eq!(row[4], 100);
690 assert_eq!(row[5], 150);
691 }
692
693 #[test]
694 #[cfg(feature = "rgb")]
695 fn to_rgb8() {
696 let data = vec![100u8, 150, 200, 255, 50, 100, 150, 255];
698 let buf = PixelBuffer::from_vec(data, 2, 1, PixelDescriptor::RGBA8_SRGB).unwrap();
699 let typed: PixelBuffer<rgb::Rgb<u8>> = buf.to_rgb8();
700 assert_eq!(typed.width(), 2);
701 assert_eq!(typed.height(), 1);
702 let slice = typed.as_slice();
703 let row = slice.row(0);
704 assert_eq!(row[0], 100);
706 assert_eq!(row[1], 150);
707 assert_eq!(row[2], 200);
708 assert_eq!(row[3], 50);
709 assert_eq!(row[4], 100);
710 assert_eq!(row[5], 150);
711 }
712
713 #[test]
714 #[cfg(feature = "rgb")]
715 fn to_rgba8() {
716 let data = vec![100u8, 150, 200, 50, 100, 150];
717 let buf = PixelBuffer::from_vec(data, 2, 1, PixelDescriptor::RGB8_SRGB).unwrap();
718 let typed: PixelBuffer<rgb::Rgba<u8>> = buf.to_rgba8();
719 assert_eq!(typed.width(), 2);
720 assert_eq!(typed.height(), 1);
721 let slice = typed.as_slice();
722 let row = slice.row(0);
723 assert_eq!(row[0], 100);
725 assert_eq!(row[1], 150);
726 assert_eq!(row[2], 200);
727 assert_eq!(row[3], 255);
728 assert_eq!(row[4], 50);
729 assert_eq!(row[5], 100);
730 assert_eq!(row[6], 150);
731 assert_eq!(row[7], 255);
732 }
733
734 #[test]
735 #[cfg(feature = "rgb")]
736 fn to_gray8() {
737 let data = vec![100u8, 150, 200, 50, 100, 150];
738 let buf = PixelBuffer::from_vec(data, 2, 1, PixelDescriptor::RGB8_SRGB).unwrap();
739 let typed: PixelBuffer<rgb::Gray<u8>> = buf.to_gray8();
740 assert_eq!(typed.width(), 2);
741 assert_eq!(typed.height(), 1);
742 let slice = typed.as_slice();
743 let row = slice.row(0);
744 assert!(row[0] > 0, "gray pixel 0 should be non-zero");
746 assert!(row[1] > 0, "gray pixel 1 should be non-zero");
747 }
748
749 #[test]
750 #[cfg(feature = "rgb")]
751 fn to_bgra8() {
752 let data = vec![100u8, 150, 200, 50, 100, 150];
753 let buf = PixelBuffer::from_vec(data, 2, 1, PixelDescriptor::RGB8_SRGB).unwrap();
754 let typed: PixelBuffer<rgb::alt::BGRA<u8>> = buf.to_bgra8();
755 assert_eq!(typed.width(), 2);
756 assert_eq!(typed.height(), 1);
757 let slice = typed.as_slice();
758 let row = slice.row(0);
759 assert_eq!(row[0], 200);
762 assert_eq!(row[1], 150);
763 assert_eq!(row[2], 100);
764 assert_eq!(row[3], 255);
765 assert_eq!(row[4], 150);
767 assert_eq!(row[5], 100);
768 assert_eq!(row[6], 50);
769 assert_eq!(row[7], 255);
770 }
771}