From a409babc5d259b7aa138eaf29a68365923dce8ea Mon Sep 17 00:00:00 2001
From: Tj Holowaychuk <tj@vision-media.ca>
Date: Wed, 17 Nov 2010 13:57:51 -0800
Subject: [PATCH 1/9] Added Image::extension()

---
 src/Image.cc | 16 ++++++++++++++++
 src/Image.h  |  8 ++++++++
 2 files changed, 24 insertions(+)

diff --git a/src/Image.cc b/src/Image.cc
index 7e6315f..2b1a122 100644
--- a/src/Image.cc
+++ b/src/Image.cc
@@ -9,6 +9,7 @@
 #include "Image.h"
 #include <stdlib.h>
 #include <string.h>
+#include <jpeglib.h>
 
 Persistent<FunctionTemplate> Image::constructor;
 
@@ -202,6 +203,7 @@ EIO_AfterLoad(eio_req *req) {
 
 void
 Image::load() {
+  printf("%d\n", extension(filename));
   if (LOADING != state) {
     // TODO: use node IO
     // Ref();
@@ -273,4 +275,18 @@ Image::loadSurface() {
   width = cairo_image_surface_get_width(_surface);
   height = cairo_image_surface_get_height(_surface);
   return cairo_surface_status(_surface);
+}
+
+/*
+ * Return UNKNOWN, JPEG, or PNG based on the filename.
+ */
+
+Image::type
+Image::extension(const char *filename) {
+  size_t len = strlen(filename);
+  filename += len;
+  if (0 == strcmp(".jpeg", filename - 5)) return Image::JPEG;
+  if (0 == strcmp(".jpg", filename - 4)) return Image::JPEG;
+  if (0 == strcmp(".png", filename - 4)) return Image::PNG;
+  return Image::UNKNOWN;
 }
\ No newline at end of file
diff --git a/src/Image.h b/src/Image.h
index e1b7c57..5034247 100644
--- a/src/Image.h
+++ b/src/Image.h
@@ -43,6 +43,14 @@ class Image: public node::ObjectWrap {
       , LOADING
       , COMPLETE
     } state;
+
+    typedef enum {
+        UNKNOWN
+      , JPEG
+      , PNG
+    } type;
+
+    static type extension(const char *filename);
   
   private:
     cairo_surface_t *_surface;

From 2c5ebd3d8200d40ade9f97c9a9e67fe317739c1f Mon Sep 17 00:00:00 2001
From: Tj Holowaychuk <tj@vision-media.ca>
Date: Wed, 17 Nov 2010 14:54:43 -0800
Subject: [PATCH 2/9] Work on jpeg loading

---
 src/Image.cc | 61 ++++++++++++++++++++++++++++++++++++++++++++++------
 wscript      |  3 ++-
 2 files changed, 57 insertions(+), 7 deletions(-)

diff --git a/src/Image.cc b/src/Image.cc
index 2b1a122..8338469 100644
--- a/src/Image.cc
+++ b/src/Image.cc
@@ -203,7 +203,6 @@ EIO_AfterLoad(eio_req *req) {
 
 void
 Image::load() {
-  printf("%d\n", extension(filename));
   if (LOADING != state) {
     // TODO: use node IO
     // Ref();
@@ -271,10 +270,60 @@ Image::error(Local<Value> err) {
 
 cairo_status_t
 Image::loadSurface() {
-  _surface = cairo_image_surface_create_from_png(filename);
-  width = cairo_image_surface_get_width(_surface);
-  height = cairo_image_surface_get_height(_surface);
-  return cairo_surface_status(_surface);
+  switch (extension(filename)) {
+    case Image::PNG:
+      _surface = cairo_image_surface_create_from_png(filename);
+      width = cairo_image_surface_get_width(_surface);
+      height = cairo_image_surface_get_height(_surface);
+      return cairo_surface_status(_surface);
+      break;
+    case Image::JPEG: {
+      // TODO: error handling
+      // TODO: move to node IO
+      FILE *stream = fopen(filename, "r");
+      struct jpeg_decompress_struct info;
+      struct jpeg_error_mgr err;
+      info.err = jpeg_std_error(&err);
+      jpeg_create_decompress(&info);
+      jpeg_stdio_src(&info, stream);
+      jpeg_read_header(&info, 1);
+      jpeg_start_decompress(&info);
+      width = info.output_width;
+      height = info.output_height;
+
+      uint8_t *data = (uint8_t *) malloc(width * height * 4);
+      uint8_t *src = (uint8_t *) malloc(width * 3);
+
+      for (int y = 0; y < height; ++y) {
+        jpeg_read_scanlines(&info, &src, 1);
+        uint32_t *row = (uint32_t *)(data + (width * 4));
+        for (int x = 0; x < width; ++x) {
+          uint32_t *pixel = row + x;
+          uint8_t r = src[x];
+          uint8_t g = src[x + 1];
+          uint8_t b = src[x + 2];
+          *pixel = 255 << 24
+            | r << 16
+            | g << 8
+            | b;
+        }
+      }
+      
+      _surface = cairo_image_surface_create_for_data(
+          data
+        , CAIRO_FORMAT_ARGB32
+        , width
+        , height
+        , width * 4);
+
+      fclose(stream);
+      jpeg_finish_decompress(&info);
+      jpeg_destroy_decompress(&info);
+      return cairo_surface_status(_surface);
+      }
+      break;
+  }
+  return CAIRO_STATUS_READ_ERROR;
 }
 
 /*
@@ -289,4 +338,4 @@ Image::extension(const char *filename) {
   if (0 == strcmp(".jpg", filename - 4)) return Image::JPEG;
   if (0 == strcmp(".png", filename - 4)) return Image::PNG;
   return Image::UNKNOWN;
-}
\ No newline at end of file
+}
diff --git a/wscript b/wscript
index 1469995..96b614a 100644
--- a/wscript
+++ b/wscript
@@ -11,6 +11,7 @@ def configure(conf):
   conf.check_tool('compiler_cxx')
   conf.check_tool('node_addon')
   conf.env.append_value('CPPFLAGS', '-DNDEBUG')
+  conf.check(lib='jpeg', uselib_store='JPEG')
   conf.check_cfg(package='cairo', mandatory=1, args='--cflags --libs')
   flags = ['-O3', '-Wall']
   conf.env.append_value('CCFLAGS', flags)
@@ -20,4 +21,4 @@ def build(bld):
   obj = bld.new_task_gen('cxx', 'shlib', 'node_addon')
   obj.target = 'canvas'
   obj.source = bld.glob('src/*.cc')
-  obj.uselib = ['CAIRO']
\ No newline at end of file
+  obj.uselib = ['CAIRO', 'JPEG']
\ No newline at end of file

From 0b227bd1e6b7c9ddd27961a9077b3c41ef291caf Mon Sep 17 00:00:00 2001
From: Tj Holowaychuk <tj@vision-media.ca>
Date: Wed, 17 Nov 2010 15:13:38 -0800
Subject: [PATCH 3/9] Todo

---
 src/Image.cc | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/Image.cc b/src/Image.cc
index 8338469..23da356 100644
--- a/src/Image.cc
+++ b/src/Image.cc
@@ -265,6 +265,7 @@ Image::error(Local<Value> err) {
 /*
  * Load cairo surface from the image src.
  * 
+ * TODO: better format resolution
  * TODO: support more formats
  */
 

From bc39108143ef57b186ef40c83697f5e39d0aedbb Mon Sep 17 00:00:00 2001
From: Tj Holowaychuk <tj@vision-media.ca>
Date: Wed, 17 Nov 2010 15:21:06 -0800
Subject: [PATCH 4/9] Misc refactoring

---
 src/Image.cc | 11 ++++-------
 1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/src/Image.cc b/src/Image.cc
index 23da356..6e31e7a 100644
--- a/src/Image.cc
+++ b/src/Image.cc
@@ -300,16 +300,13 @@ Image::loadSurface() {
         uint32_t *row = (uint32_t *)(data + (width * 4));
         for (int x = 0; x < width; ++x) {
           uint32_t *pixel = row + x;
-          uint8_t r = src[x];
-          uint8_t g = src[x + 1];
-          uint8_t b = src[x + 2];
           *pixel = 255 << 24
-            | r << 16
-            | g << 8
-            | b;
+            | src[x + 0] << 16
+            | src[x + 1] << 8
+            | src[x + 2];
         }
       }
-      
+
       _surface = cairo_image_surface_create_for_data(
           data
         , CAIRO_FORMAT_ARGB32

From c9c6955c91f5ac4b716807123a22e7e44ed70011 Mon Sep 17 00:00:00 2001
From: Tj Holowaychuk <tj@vision-media.ca>
Date: Thu, 18 Nov 2010 09:17:19 -0800
Subject: [PATCH 5/9] stride

---
 src/Image.cc | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/Image.cc b/src/Image.cc
index 6e31e7a..eb6f164 100644
--- a/src/Image.cc
+++ b/src/Image.cc
@@ -292,12 +292,13 @@ Image::loadSurface() {
       width = info.output_width;
       height = info.output_height;
 
+      int stride = width * 4;
       uint8_t *data = (uint8_t *) malloc(width * height * 4);
       uint8_t *src = (uint8_t *) malloc(width * 3);
 
       for (int y = 0; y < height; ++y) {
         jpeg_read_scanlines(&info, &src, 1);
-        uint32_t *row = (uint32_t *)(data + (width * 4));
+        uint32_t *row = (uint32_t *)(data + stride * y);
         for (int x = 0; x < width; ++x) {
           uint32_t *pixel = row + x;
           *pixel = 255 << 24

From 3c747204fad3686982af6170df1f4942dbf3f25a Mon Sep 17 00:00:00 2001
From: Tj Holowaychuk <tj@vision-media.ca>
Date: Thu, 18 Nov 2010 09:27:13 -0800
Subject: [PATCH 6/9] Fixed jpeg loading

---
 src/Image.cc | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/Image.cc b/src/Image.cc
index eb6f164..3fe99c0 100644
--- a/src/Image.cc
+++ b/src/Image.cc
@@ -302,9 +302,9 @@ Image::loadSurface() {
         for (int x = 0; x < width; ++x) {
           uint32_t *pixel = row + x;
           *pixel = 255 << 24
-            | src[x + 0] << 16
-            | src[x + 1] << 8
-            | src[x + 2];
+            | src[3 * x + 0] << 16
+            | src[3 * x + 1] << 8
+            | src[3 * x + 2];
         }
       }
 
@@ -314,7 +314,7 @@ Image::loadSurface() {
         , width
         , height
         , width * 4);
-
+cairo_surface_write_to_png(_surface, "test2.png");
       fclose(stream);
       jpeg_finish_decompress(&info);
       jpeg_destroy_decompress(&info);

From dfa5d630ab2048d8635cf75822d7da9ac1199bbe Mon Sep 17 00:00:00 2001
From: Tj Holowaychuk <tj@vision-media.ca>
Date: Thu, 18 Nov 2010 09:31:06 -0800
Subject: [PATCH 7/9] bx

---
 src/Image.cc | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/Image.cc b/src/Image.cc
index 3fe99c0..0c289f6 100644
--- a/src/Image.cc
+++ b/src/Image.cc
@@ -300,11 +300,12 @@ Image::loadSurface() {
         jpeg_read_scanlines(&info, &src, 1);
         uint32_t *row = (uint32_t *)(data + stride * y);
         for (int x = 0; x < width; ++x) {
+          int bx = 3 * x;
           uint32_t *pixel = row + x;
           *pixel = 255 << 24
-            | src[3 * x + 0] << 16
-            | src[3 * x + 1] << 8
-            | src[3 * x + 2];
+            | src[bx + 0] << 16
+            | src[bx + 1] << 8
+            | src[bx + 2];
         }
       }
 

From 8b64b12f56b0a2e9a9f88953d1a9ef47c6d58be1 Mon Sep 17 00:00:00 2001
From: Tj Holowaychuk <tj@vision-media.ca>
Date: Thu, 18 Nov 2010 09:38:20 -0800
Subject: [PATCH 8/9] Added width x height to Image#inspect()

---
 lib/image.js | 1 +
 src/Image.cc | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/lib/image.js b/lib/image.js
index be0658b..231d6be 100644
--- a/lib/image.js
+++ b/lib/image.js
@@ -21,6 +21,7 @@ var Canvas = require('../build/default/canvas')
 
 Image.prototype.inspect = function(){
   return '[Image'
+    + (this.complete ? ':' + this.width + 'x' + this.height : '')
     + (this.src ? ' ' + this.src : '')
     + (this.complete ? ' complete' : '')
     + ']';
diff --git a/src/Image.cc b/src/Image.cc
index 0c289f6..df4d392 100644
--- a/src/Image.cc
+++ b/src/Image.cc
@@ -315,7 +315,7 @@ Image::loadSurface() {
         , width
         , height
         , width * 4);
-cairo_surface_write_to_png(_surface, "test2.png");
+
       fclose(stream);
       jpeg_finish_decompress(&info);
       jpeg_destroy_decompress(&info);

From 41b08746ebb90dff269a5dd53c2423c675a37569 Mon Sep 17 00:00:00 2001
From: Tj Holowaychuk <tj@vision-media.ca>
Date: Thu, 18 Nov 2010 10:13:47 -0800
Subject: [PATCH 9/9] Added Image::loadJPEG() and Image::loadPNG()

---
 src/Image.cc | 115 ++++++++++++++++++++++++++++-----------------------
 src/Image.h  |   2 +
 2 files changed, 66 insertions(+), 51 deletions(-)

diff --git a/src/Image.cc b/src/Image.cc
index df4d392..80a218f 100644
--- a/src/Image.cc
+++ b/src/Image.cc
@@ -265,67 +265,80 @@ Image::error(Local<Value> err) {
 /*
  * Load cairo surface from the image src.
  * 
- * TODO: better format resolution
+ * TODO: better format detection
  * TODO: support more formats
  */
 
 cairo_status_t
 Image::loadSurface() {
   switch (extension(filename)) {
-    case Image::PNG:
-      _surface = cairo_image_surface_create_from_png(filename);
-      width = cairo_image_surface_get_width(_surface);
-      height = cairo_image_surface_get_height(_surface);
-      return cairo_surface_status(_surface);
-      break;
-    case Image::JPEG: {
-      // TODO: error handling
-      // TODO: move to node IO
-      FILE *stream = fopen(filename, "r");
-      struct jpeg_decompress_struct info;
-      struct jpeg_error_mgr err;
-      info.err = jpeg_std_error(&err);
-      jpeg_create_decompress(&info);
-      jpeg_stdio_src(&info, stream);
-      jpeg_read_header(&info, 1);
-      jpeg_start_decompress(&info);
-      width = info.output_width;
-      height = info.output_height;
-
-      int stride = width * 4;
-      uint8_t *data = (uint8_t *) malloc(width * height * 4);
-      uint8_t *src = (uint8_t *) malloc(width * 3);
-
-      for (int y = 0; y < height; ++y) {
-        jpeg_read_scanlines(&info, &src, 1);
-        uint32_t *row = (uint32_t *)(data + stride * y);
-        for (int x = 0; x < width; ++x) {
-          int bx = 3 * x;
-          uint32_t *pixel = row + x;
-          *pixel = 255 << 24
-            | src[bx + 0] << 16
-            | src[bx + 1] << 8
-            | src[bx + 2];
-        }
-      }
-
-      _surface = cairo_image_surface_create_for_data(
-          data
-        , CAIRO_FORMAT_ARGB32
-        , width
-        , height
-        , width * 4);
-
-      fclose(stream);
-      jpeg_finish_decompress(&info);
-      jpeg_destroy_decompress(&info);
-      return cairo_surface_status(_surface);
-      }
-      break;
+    case Image::PNG: return loadPNG();
+    case Image::JPEG: return loadJPEG();
   }
   return CAIRO_STATUS_READ_ERROR;
 }
 
+/*
+ * Load PNG.
+ */
+
+cairo_status_t
+Image::loadPNG() {
+  _surface = cairo_image_surface_create_from_png(filename);
+  width = cairo_image_surface_get_width(_surface);
+  height = cairo_image_surface_get_height(_surface);
+  return cairo_surface_status(_surface);
+}
+
+/*
+ * Load JPEG, convert RGB to ARGB.
+ */
+
+cairo_status_t
+Image::loadJPEG() {
+  // TODO: error handling
+  // TODO: move to node IO
+  FILE *stream = fopen(filename, "r");
+  struct jpeg_decompress_struct info;
+  struct jpeg_error_mgr err;
+  info.err = jpeg_std_error(&err);
+  jpeg_create_decompress(&info);
+  jpeg_stdio_src(&info, stream);
+  jpeg_read_header(&info, 1);
+  jpeg_start_decompress(&info);
+  width = info.output_width;
+  height = info.output_height;
+
+  int stride = width * 4;
+  uint8_t *data = (uint8_t *) malloc(width * height * 4);
+  uint8_t *src = (uint8_t *) malloc(width * 3);
+
+  for (int y = 0; y < height; ++y) {
+    jpeg_read_scanlines(&info, &src, 1);
+    uint32_t *row = (uint32_t *)(data + stride * y);
+    for (int x = 0; x < width; ++x) {
+      int bx = 3 * x;
+      uint32_t *pixel = row + x;
+      *pixel = 255 << 24
+        | src[bx + 0] << 16
+        | src[bx + 1] << 8
+        | src[bx + 2];
+    }
+  }
+
+  _surface = cairo_image_surface_create_for_data(
+      data
+    , CAIRO_FORMAT_ARGB32
+    , width
+    , height
+    , width * 4);
+
+  fclose(stream);
+  jpeg_finish_decompress(&info);
+  jpeg_destroy_decompress(&info);
+  return cairo_surface_status(_surface);
+}
+
 /*
  * Return UNKNOWN, JPEG, or PNG based on the filename.
  */
diff --git a/src/Image.h b/src/Image.h
index 5034247..06035aa 100644
--- a/src/Image.h
+++ b/src/Image.h
@@ -32,6 +32,8 @@ class Image: public node::ObjectWrap {
     inline uint8_t *data(){ return cairo_image_surface_get_data(_surface); } 
     inline int stride(){ return cairo_image_surface_get_stride(_surface); } 
     cairo_status_t loadSurface();
+    cairo_status_t loadPNG();
+    cairo_status_t loadJPEG();
     void error(Local<Value>);
     void loadSync();
     void loaded();