aboutsummaryrefslogtreecommitdiffstats
path: root/components/net/image
diff options
context:
space:
mode:
Diffstat (limited to 'components/net/image')
-rw-r--r--components/net/image/base.rs67
-rw-r--r--components/net/image/holder.rs109
-rw-r--r--components/net/image/test.jpegbin0 -> 4962 bytes
3 files changed, 176 insertions, 0 deletions
diff --git a/components/net/image/base.rs b/components/net/image/base.rs
new file mode 100644
index 00000000000..deda4ee8556
--- /dev/null
+++ b/components/net/image/base.rs
@@ -0,0 +1,67 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::iter::range_step;
+use stb_image = stb_image::image;
+use png;
+
+// FIXME: Images must not be copied every frame. Instead we should atomically
+// reference count them.
+pub type Image = png::Image;
+
+
+static TEST_IMAGE: &'static [u8] = include_bin!("test.jpeg");
+
+pub fn test_image_bin() -> Vec<u8> {
+ TEST_IMAGE.iter().map(|&x| x).collect()
+}
+
+// TODO(pcwalton): Speed up with SIMD, or better yet, find some way to not do this.
+fn byte_swap(data: &mut [u8]) {
+ let length = data.len();
+ for i in range_step(0, length, 4) {
+ let r = data[i + 2];
+ data[i + 2] = data[i + 0];
+ data[i + 0] = r;
+ }
+}
+
+pub fn load_from_memory(buffer: &[u8]) -> Option<Image> {
+ if buffer.len() == 0 {
+ return None;
+ }
+
+ if png::is_png(buffer) {
+ match png::load_png_from_memory(buffer) {
+ Ok(mut png_image) => {
+ match png_image.pixels {
+ png::RGB8(ref mut data) | png::RGBA8(ref mut data) => {
+ byte_swap(data.as_mut_slice());
+ }
+ _ => {}
+ }
+ Some(png_image)
+ }
+ Err(_err) => None,
+ }
+ } else {
+ // For non-png images, we use stb_image
+ // Can't remember why we do this. Maybe it's what cairo wants
+ static FORCE_DEPTH: uint = 4;
+
+ match stb_image::load_from_memory_with_depth(buffer, FORCE_DEPTH, true) {
+ stb_image::ImageU8(mut image) => {
+ assert!(image.depth == 4);
+ byte_swap(image.data.as_mut_slice());
+ Some(png::Image {
+ width: image.width as u32,
+ height: image.height as u32,
+ pixels: png::RGBA8(image.data)
+ })
+ }
+ stb_image::ImageF32(_image) => fail!("HDR images not implemented"),
+ stb_image::Error(_) => None
+ }
+ }
+}
diff --git a/components/net/image/holder.rs b/components/net/image/holder.rs
new file mode 100644
index 00000000000..11f055aad9d
--- /dev/null
+++ b/components/net/image/holder.rs
@@ -0,0 +1,109 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use image::base::Image;
+use image_cache_task::{ImageReady, ImageNotReady, ImageFailed};
+use local_image_cache::LocalImageCache;
+
+use geom::size::Size2D;
+use std::mem;
+use sync::{Arc, Mutex};
+use url::Url;
+
+// FIXME: Nasty coupling here This will be a problem if we want to factor out image handling from
+// the network stack. This should probably be factored out into an interface and use dependency
+// injection.
+
+/// A struct to store image data. The image will be loaded once the first time it is requested,
+/// and an Arc will be stored. Clones of this Arc are given out on demand.
+#[deriving(Clone)]
+pub struct ImageHolder {
+ url: Url,
+ image: Option<Arc<Box<Image>>>,
+ cached_size: Size2D<int>,
+ local_image_cache: Arc<Mutex<LocalImageCache>>,
+}
+
+impl ImageHolder {
+ pub fn new(url: Url, local_image_cache: Arc<Mutex<LocalImageCache>>) -> ImageHolder {
+ debug!("ImageHolder::new() {}", url.serialize());
+ let holder = ImageHolder {
+ url: url,
+ image: None,
+ cached_size: Size2D(0,0),
+ local_image_cache: local_image_cache.clone(),
+ };
+
+ // Tell the image cache we're going to be interested in this url
+ // FIXME: These two messages must be sent to prep an image for use
+ // but they are intended to be spread out in time. Ideally prefetch
+ // should be done as early as possible and decode only once we
+ // are sure that the image will be used.
+ {
+ let val = holder.local_image_cache.lock();
+ let mut local_image_cache = val;
+ local_image_cache.prefetch(&holder.url);
+ local_image_cache.decode(&holder.url);
+ }
+
+ holder
+ }
+
+ /// This version doesn't perform any computation, but may be stale w.r.t. newly-available image
+ /// data that determines size.
+ ///
+ /// The intent is that the impure version is used during layout when dimensions are used for
+ /// computing layout.
+ pub fn size(&self) -> Size2D<int> {
+ self.cached_size
+ }
+
+ /// Query and update the current image size.
+ pub fn get_size(&mut self) -> Option<Size2D<int>> {
+ debug!("get_size() {}", self.url.serialize());
+ self.get_image().map(|img| {
+ self.cached_size = Size2D(img.width as int,
+ img.height as int);
+ self.cached_size.clone()
+ })
+ }
+
+ pub fn get_image_if_present(&self) -> Option<Arc<Box<Image>>> {
+ debug!("get_image_if_present() {}", self.url.serialize());
+ self.image.clone()
+ }
+
+ pub fn get_image(&mut self) -> Option<Arc<Box<Image>>> {
+ debug!("get_image() {}", self.url.serialize());
+
+ // If this is the first time we've called this function, load
+ // the image and store it for the future
+ if self.image.is_none() {
+ let port = {
+ let val = self.local_image_cache.lock();
+ let mut local_image_cache = val;
+ local_image_cache.get_image(&self.url)
+ };
+ match port.recv() {
+ ImageReady(image) => {
+ self.image = Some(image);
+ }
+ ImageNotReady => {
+ debug!("image not ready for {:s}", self.url.serialize());
+ }
+ ImageFailed => {
+ debug!("image decoding failed for {:s}", self.url.serialize());
+ }
+ }
+ }
+
+ // Clone isn't pure so we have to swap out the mutable image option
+ let image = mem::replace(&mut self.image, None);
+ let result = image.clone();
+ mem::replace(&mut self.image, image);
+
+ return result;
+ }
+}
+
diff --git a/components/net/image/test.jpeg b/components/net/image/test.jpeg
new file mode 100644
index 00000000000..1a0bdb7acd1
--- /dev/null
+++ b/components/net/image/test.jpeg
Binary files differ