问题
I am using the Piston image package to generate images.
fn get_image() -> image::DynamicImage {
image::DynamicImage::ImageRgba8(image::ImageBuffer::new(512, 512))
}
I have a hyper web server, from which I would like to serve dynamically-generated images.
Based on the answer to How to create an in-memory object that can be used as a Reader or Writer in Rust?, I thought I might be able to use a Cursor<Vec<u8>>
as the destination. However, image
only seems to provide a method to write to a filename, not a Writer
like my cursor.
After looking through image
's documentation, I hoped there might be some way to use the image::png::PNGEncoder struct directly. It provides a public method encode(self, data: &[u8], width: u32, height: u32, color: ColorType) -> Result<()>
. However, I wasn't sure what the data
argument is supposed to be, and I couldn't find any public declarations of the ColorType
s used by ImageBuffer
.
(&Get, "/image.png") => {
let v = {
let generated = get_image();
let mut encoded_image = Cursor::new(Vec::new());
let (width, heigth) = generated.dimensions();
{
let encoder = image::png::PNGEncoder::new(&encoded_image);
encoder.encode(unimplemented!("what goes here?"));
}
encoded_image.get_mut()
};
Box::new(futures::future::ok(
Response::new()
.with_header(ContentLength(v.len() as u64))
.with_body(v.clone()),
))
}
How do I encode a Piston GenericImage to a format like PNG and get the result in memory?
回答1:
fn getImage() -> image::DynamicImage
You have a DynamicImage
.
image only seems to provide a method to write to a filename
I assume you mean DynamicImage::save.
The method immediately before save
is write_to:
pub fn write_to<W: Write, F: Into<ImageOutputFormat>>(
&self,
w: &mut W,
format: F
) -> ImageResult<()>
This accepts any generic writer:
let mut buf = Vec::new();
get_image()
.write_to(&mut buf, image::ImageOutputFormat::PNG)
.expect("Unable to write");
There's no need for a Cursor
.
How do I encode a Piston
GenericImage
to a format like PNG?
I have no idea the best way to do this. I'd probably copy the generic image into a DynamicImage
and then follow the above instructions.
回答2:
I've adapted OP's code based on the image
crate docs. It looks possible to make the original code to work. There is no need for Cursor
but one needs to create a "by reference" adaptor for the instance of Write
implemented by Vec
.
// This code have not been tested or even compiled
let v = {
let generated = get_image();
let mut encoded_image = Vec::new();
let (width, height) = generated.dimensions();
{
image::png::PNGEncoder::new(encoded_image.by_ref())
.encode(
generated.raw_pixels(),
width,
height,
generated.color()
).expected("error encoding pixels as PNG");
}
encoded_image
};
Here is the code I actually used in my project. I get a reference to the raw pixels passed in as an &[u8]
slice. Each pixel represents an 8 bit grayscale value. Here is a function that encodes the pixels into an in memory PNG image.
pub fn create_png(pixels: &[u8], dimensions: (usize, usize)) -> Result<Vec<u8>> {
let mut png_buffer = Vec::new();
PNGEncoder::new(png_buffer.by_ref())
.encode(
pixels,
dimensions.0 as u32,
dimensions.1 as u32,
ColorType::Gray(8),
).expect("error encoding pixels as PNG");
Ok(png_buffer)
}
来源:https://stackoverflow.com/questions/50731636/how-do-i-encode-a-rust-piston-image-and-get-the-result-in-memory