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?
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.
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)
}