我有一个基本的结构体ImageData
,它主要保存一个表示PNG图像的u8s (bytes
)数组(布局为rgbrbrgb…或rgbargbargba…):
struct ImageData {
png_type: png::ColorType,
bytes: Vec<u8>,
width: u32,
height: u32,
is_srgb: bool,
}
我也有一个PixelValue
结构体,这将允许我类似地对待RGB和RGBA -也就是说,我想得到PixelValue
s而不是3或4个u8
s的数组,因为这样我就会复制很多代码来处理那些不同的类型。
struct PixelValue {
r: u8,
g: u8,
b: u8,
a: u8,
}
所以我想在ImageData
上实现IntoIterator
,它将在PixelValue
上返回一个迭代器。它应该将u8
块转换为PixelValue
s:
impl<'a> IntoIterator for &'a ImageData {
type Item = PixelValue;
type IntoIter = Iter<PixelValue>; // this is definitely wrong
fn into_iter(self) -> Self::IntoIter {
let pixel_size = get_pixel_size(self.png_type); // 3 or 4, depending on RGB or RGBA
// Need to write something that can take an array of 3 or 4 u8s and returns the PixelValue
self.bytes.chunks(pixel_size).map(|ar| PixelValue(ar))
}
}
我的问题是:
- 我走的方向对吗?我可以用
map
改变迭代器的输出值(Item
)吗?如果不是,为什么不呢? - 是否有明显的"最佳实践"?如何解决这个问题(将像素RGB或rgba的数组转换为结构体)?
您可以使用动态分派,将迭代器包装在Box
中:
struct ImageData {
png_type: u8,
bytes: Vec<u8>,
width: u32,
height: u32,
is_srgb: bool,
}
struct PixelValue {
r: u8,
g: u8,
b: u8,
a: u8,
}
impl<'a> IntoIterator for &'a ImageData {
type Item = PixelValue;
type IntoIter = Box<dyn Iterator<Item = Self::Item> + 'a>;
fn into_iter(self) -> Self::IntoIter {
let size = 3;
Box::new(self.bytes.chunks(size).map(move |chunk| match size {
3 => PixelValue {
r: chunk[0],
g: chunk[1],
b: chunk[2],
a: 255,
},
_ => PixelValue {
r: chunk[0],
g: chunk[1],
b: chunk[2],
a: chunk[3],
},
}))
}
}
游乐场
请注意,在这个例子中,我更改了一些我没有访问权限的类型(png_type
和get_size
方法)。
您可以创建实现Iterator
的ImageDataIntoPixelValueIterator
,并将其用作type IntoIter
。注:我已经从ImageData
中删除了png_type
,以便使此代码可重现,并假装alpha通道始终存在:
struct ImageData {
bytes: Vec<u8>,
width: u32,
height: u32,
is_srgb: bool,
}
struct PixelValue {
r: u8,
g: u8,
b: u8,
a: u8,
}
impl IntoIterator for ImageData {
type Item = PixelValue;
type IntoIter = ImageDataIntoPixelValueIterator;
fn into_iter(self) -> Self::IntoIter {
ImageDataIntoPixelValueIterator {
bytes: self.bytes,
index: 0,
}
}
}
struct ImageDataIntoPixelValueIterator {
bytes: Vec<u8>,
index: usize,
}
impl Iterator for ImageDataIntoPixelValueIterator {
type Item = PixelValue;
fn next(&mut self) -> Option<PixelValue> {
// assumes that self.bytes.len() is a multiple of four.
// this should probably be asserted somewhere else
if self.index >= self.bytes.len() {
None
} else {
let res = PixelValue {
r: self.bytes[self.index],
g: self.bytes[self.index + 1],
b: self.bytes[self.index + 2],
a: self.bytes[self.index + 3],
};
self.index += 4;
Some(res)
}
}
}
游乐场
我已经根据你的具体需求基本实现了这个答案。请仔细阅读,因为它提供了丰富的解释性信息,帮助你理解为什么某些事情会或不会工作。