Browse Source

Merge branch 'master' of https://github.com/Automattic/node-canvas into fix/lighten

Conflicts:
	src/CanvasRenderingContext2d.cc
v1.x
nulltask 11 years ago
parent
commit
3e9464984e
  1. 1
      .gitignore
  2. 12
      .travis.yml
  3. 329
      History.md
  4. 82
      Readme.md
  5. 41
      binding.gyp
  6. 22
      examples/small-svg.js
  7. 14
      install
  8. 2
      lib/canvas.js
  9. 64
      package.json
  10. 117
      src/Canvas.cc
  11. 6
      src/Canvas.h
  12. 12
      src/CanvasGradient.cc
  13. 1
      src/CanvasGradient.h
  14. 10
      src/CanvasPattern.cc
  15. 1
      src/CanvasPattern.h
  16. 269
      src/CanvasRenderingContext2d.cc
  17. 4
      src/CanvasRenderingContext2d.h
  18. 12
      src/FontFace.cc
  19. 54
      src/Image.cc
  20. 9
      src/Image.h
  21. 18
      src/ImageData.cc
  22. 25
      src/JPEGStream.h
  23. 18
      src/PixelArray.cc
  24. 6
      src/closure.h
  25. 4
      src/color.cc
  26. 8
      src/init.cc
  27. 6
      test/canvas.test.js
  28. 67
      test/public/tests.js

1
.gitignore

@ -7,6 +7,7 @@ examples/*.jpg
testing
out.png
out.pdf
out.svg
.pomo
node_modules

12
.travis.yml

@ -1,7 +1,9 @@
language: node_js
node_js:
- 0.4
- 0.6
- 0.7 # development version of 0.8, may be unstable
- 0.8
- 0.9
- '0.6'
- '0.8'
- '0.10'
- '0.11'
before_install:
- sudo chown -R $USER /usr/local
- sh install

329
History.md

@ -1,10 +1,49 @@
1.1.6 / 2014-08-01
==================
* export canvas.CanvasPixelArray instead of canvas.PixelArray which is undefined
* Glib version test into giflib exists test
* Giflib 5.1
* install: use an even older version of giflib (v4.1.6)
* install: use an older version of giflib (v4.2.3)
* install: install `giflib`
* install: use more compatible sh syntax
* travis: attempt to run the ./install script before testintg
* travis: test node v0.6, v0.8, v0.10, and v0.11
* Distinguish between 'add' and 'lighter'
1.1.5 / 2014-06-26
==================
* Readme: remove Contributors section
* Readme: update copyright
* On Windows, copy required DLLs next to ".node" file (#442 @pandell)
* Duplicate "msvc_settings" for "Debug" configuration
* Remove unneeded #include <nan.h>
* Use float constants to prevent double->float conversion warning
* Ignore Visual C++ 2013 warnings (#441 @pandell)
* Add algorithm include to CanvasRenderingContext2d.cc for std::min (#435 @kkoopa)
* Updated NAN to 1.2.0 (#434 @kkoopa)
1.1.4 / 2014-06-08
==================
* Fix compile error with Visual C++
* Add support for the lineDash API
* Update NAN
* New V8 compatibility
* Correctly limit bounds in PutImageData to prevent segment fault
* Fix segfault when onload and onerror are not function
* Add support for Node 0.11.9
1.1.3 / 2014-01-08
==================
* Add CAIRO_FORMAT_INVALID
* Readjust the amount of allocated memory
* Fix argument index for filter parameter
* Make has_lib.sh work properly on Debian 64bit
* Add CAIRO_FORMAT_INVALID
* Readjust the amount of allocated memory
* Fix argument index for filter parameter
* Make has_lib.sh work properly on Debian 64bit
1.1.2 / 2013-10-31
==================
@ -42,337 +81,337 @@
1.0.2 / 2013-03-22
==================
* add Context2d#imageSmoothingEnabled=
* add Context2d#imageSmoothingEnabled=
1.0.1 / 2013-02-25
==================
* travis: test modern node versions
* change the node-gyp build to use pkg-config
* travis: test modern node versions
* change the node-gyp build to use pkg-config
1.0.0 / 2013-01-16
==================
* add conditional pango font support [Julian Viereck]
* add `Canvas#{png,jpeg}Stream()` alias of create* legacy methods
* add support for grayscale JPEGs
* fix: explicitly cast the after work callback function to "uv_after_work_cb"
* fix test server for express 3.x
* fix: call cairo_surface_finish in ~Canvas when pdf
* remove old 0.4.x binding support. Closes #197
* add conditional pango font support [Julian Viereck]
* add `Canvas#{png,jpeg}Stream()` alias of create* legacy methods
* add support for grayscale JPEGs
* fix: explicitly cast the after work callback function to "uv_after_work_cb"
* fix test server for express 3.x
* fix: call cairo_surface_finish in ~Canvas when pdf
* remove old 0.4.x binding support. Closes #197
0.13.1 / 2012-08-20
==================
* fix cases where GIF_LIB_VERSION is not defined
* fix auto-detection of optional libraries for OS X
* fix Context2d::SetFont for pango when setting normal weight/style
* fix cases where GIF_LIB_VERSION is not defined
* fix auto-detection of optional libraries for OS X
* fix Context2d::SetFont for pango when setting normal weight/style
0.13.0 / 2012-08-12
==================
* add pango support [c-spencer]
* add pango / png / jpeg gyp auto-detection [c-spencer]
* add `.gifVersion` [tootallnate]
* add `.jpegVersion` [tootallnate]
* add moar gyp stuff [tootallnate]
* remove wscript
* fix `closure_destroy()` with cast for `AdjustAmountOfExternalAllocatedMemory()`
* add pango support [c-spencer]
* add pango / png / jpeg gyp auto-detection [c-spencer]
* add `.gifVersion` [tootallnate]
* add `.jpegVersion` [tootallnate]
* add moar gyp stuff [tootallnate]
* remove wscript
* fix `closure_destroy()` with cast for `AdjustAmountOfExternalAllocatedMemory()`
0.12.1 / 2012-06-29
==================
* fix jpeg malloc Image issue. Closes #160 [c-spencer]
* Improve Image mode API
* Add clearData method to handle reassignment of src, and clean up mime data memory handling.
* Improve how _data_len is managed and use to adjust memory, hide more of mime API behind cairo version conditional.
* Add optional mime-data tracking to Image.
* Refactor JPEG decoding into decodeJPEGIntoSurface
* fix jpeg malloc Image issue. Closes #160 [c-spencer]
* Improve Image mode API
* Add clearData method to handle reassignment of src, and clean up mime data memory handling.
* Improve how _data_len is managed and use to adjust memory, hide more of mime API behind cairo version conditional.
* Add optional mime-data tracking to Image.
* Refactor JPEG decoding into decodeJPEGIntoSurface
0.12.0 / 2012-05-02
==================
* Added `textDrawingMode` context property [c-spencer]
* Added additional TextMetrics properties [c-spencer]
* Added `textDrawingMode` context property [c-spencer]
* Added additional TextMetrics properties [c-spencer]
0.11.3 / 2012-04-25
==================
* Fixed `Image` memory leak. Closes #150
* Fixed Context2d::hasShadow()
* Fixed `Image` memory leak. Closes #150
* Fixed Context2d::hasShadow()
0.11.2 / 2012-04-12
==================
* Fixed: pdf memory leak, free closure and surface in ~Canvas
* Fixed: pdf memory leak, free closure and surface in ~Canvas
0.11.1 / 2012-04-10
==================
* Changed: renamed .nextPage() to .addPage()
* Changed: renamed .nextPage() to .addPage()
0.11.0 / 2012-04-10
==================
* Added quick PDF support
* Added `Canvas#type` getter
* Added ./examples/pdf-images.js
* Added ./examples/multiple-page-pdf.js
* Added ./examples/small-pdf.js
* Added quick PDF support
* Added `Canvas#type` getter
* Added ./examples/pdf-images.js
* Added ./examples/multiple-page-pdf.js
* Added ./examples/small-pdf.js
0.10.3 / 2012-02-27
==================
* Fixed quadratic curve starting point for undefined path. Closes #155
* Fixed quadratic curve starting point for undefined path. Closes #155
0.10.2 / 2012-02-06
==================
* Fixed: Context2d setters with invalid values ignored
* Changed: replaced seek with `fstat()`
* Fixed: Context2d setters with invalid values ignored
* Changed: replaced seek with `fstat()`
0.10.1 / 2012-01-31
==================
* Added _/opt/local/lib_ to wscript [obarthel]
* Added bounds checking to `rgba_to_string()` [obarthel]
* Fixed cleanup in JPEG Image loading [obarthel]
* Fixed missing CSS color table values [obarthel]
* Added _/opt/local/lib_ to wscript [obarthel]
* Added bounds checking to `rgba_to_string()` [obarthel]
* Fixed cleanup in JPEG Image loading [obarthel]
* Fixed missing CSS color table values [obarthel]
0.10.0 / 2012-01-18
==================
* Added `ctx.createPattern()` [slaskis]
* Added `ctx.createPattern()` [slaskis]
0.9.0 / 2012-01-13
==================
* Added `createJPEGStream()` [Elijah Hamovitz]
* Added `createJPEGStream()` [Elijah Hamovitz]
0.8.3 / 2012-01-04
==================
* Added support for libjpeg62-dev or libjpeg8-dev [wwlinx]
* Added support for libjpeg62-dev or libjpeg8-dev [wwlinx]
0.8.2 / 2011-12-14
==================
* Fixed two memory leaks in context2d [Tharit]
* Fixed `make test-server`
* Fixed two memory leaks in context2d [Tharit]
* Fixed `make test-server`
0.8.1 / 2011-10-31
==================
* Added 0.5.x support [TooTallNate]
* Fixed `measureText().width`. Closes #126
* Added 0.5.x support [TooTallNate]
* Fixed `measureText().width`. Closes #126
0.8.0 / 2011-10-28
==================
* Added data uri support. Closes #49
* Added data uri support. Closes #49
0.7.3 / 2011-09-14
==================
* Added better lineTo() / moveTo() exception messages
* Added better lineTo() / moveTo() exception messages
0.7.2 / 2011-08-30
==================
* Changed: prefix some private methods with _
* Changed: prefix some private methods with _
0.7.1 / 2011-08-25
==================
* Added better image format detection
* Added libpath options to waf configuration; this was necessary to correctly detect gif and jpeg support on FreeBSD
* Added better image format detection
* Added libpath options to waf configuration; this was necessary to correctly detect gif and jpeg support on FreeBSD
0.7.0 / 2011-07-12
==================
* Added GIF support [Brian McKinney]
* Added GIF support [Brian McKinney]
0.6.0 / 2011-06-04
==================
* Added `Image#src=Buffer` support. Closes #91
* Added `devDependencies`
* Added `source-atop` test
* Added _image-src.js_ example
* Removed `V8::AdjustAmountOfExternalAllocatedMemory()` call from `toBuffer()`
* Fixed v8 memory hint when resizing canvas [atomizer]
* Added `Image#src=Buffer` support. Closes #91
* Added `devDependencies`
* Added `source-atop` test
* Added _image-src.js_ example
* Removed `V8::AdjustAmountOfExternalAllocatedMemory()` call from `toBuffer()`
* Fixed v8 memory hint when resizing canvas [atomizer]
0.5.4 / 2011-04-20
==================
* Added; special case of zero-width rectangle [atomizer]
* Fixed; do not clamp arguments to integer values [atomizer]
* Fixed; preserve current path during `fillRect()` and `strokeRect()` [atomizer]
* Fixed; `restorePath()`: clear current path before appending [atomizer]
* Added; special case of zero-width rectangle [atomizer]
* Fixed; do not clamp arguments to integer values [atomizer]
* Fixed; preserve current path during `fillRect()` and `strokeRect()` [atomizer]
* Fixed; `restorePath()`: clear current path before appending [atomizer]
0.5.3 / 2011-04-11
==================
* Clamp image bounds in `PixelArray::PixelArray()` [Marcello Bastea-Forte]
* Clamp image bounds in `PixelArray::PixelArray()` [Marcello Bastea-Forte]
0.5.2 / 2011-04-09
==================
* Changed; make `PNGStream` a real `Stream` [Marcello Bastea-Forte]
* Changed; make `PNGStream` a real `Stream` [Marcello Bastea-Forte]
0.5.1 / 2011-03-16
==================
* Fixed (kinda) `img.src=` error handling
* Fixed; move closure.h down for malloc ref. Closes #80
* Fixed (kinda) `img.src=` error handling
* Fixed; move closure.h down for malloc ref. Closes #80
0.5.0 / 2011-03-14
==================
* Added several more operators (color-dodge, color-burn, difference, etc)
* Performance; no longer re-allocating `closure->data` for each png write
* Fixed freeing of `Context2d` states
* Fixed text alignment / baseline [Olaf]
* Fixed HandleScopes [Olaf]
* Fixed small misc memory leaks
* Fixed `Buffer` usage for node 0.4.x
* Added several more operators (color-dodge, color-burn, difference, etc)
* Performance; no longer re-allocating `closure->data` for each png write
* Fixed freeing of `Context2d` states
* Fixed text alignment / baseline [Olaf]
* Fixed HandleScopes [Olaf]
* Fixed small misc memory leaks
* Fixed `Buffer` usage for node 0.4.x
0.4.3 / 2011-01-11
==================
* Fixed font family dereferencing. Closes #72
* Fixed; stripping of quotes from font-family before applying
* Fixed duplicate textAlign getter
* Removed sans-serif default of _Arial_
* Fixed font family dereferencing. Closes #72
* Fixed; stripping of quotes from font-family before applying
* Fixed duplicate textAlign getter
* Removed sans-serif default of _Arial_
0.4.2 / 2010-12-28
==================
* Fixed font size growing issue after successive calls. Closes #70
* Fixed font size growing issue after successive calls. Closes #70
0.4.1 / 2010-12-18
==================
* Fixed; toString() first argument of `{fill,stroke}Text()`. Closes #68
* Fixed; toString() first argument of `{fill,stroke}Text()`. Closes #68
0.4.0 / 2010-12-12
==================
* Added `drawImage()` with `Canvas` instance support. Closes #67
* Added `drawImage()` with `Canvas` instance support. Closes #67
0.3.3 / 2010-11-30
==================
* Added `CanvasRenderingContext2d#patternQuality` accessor, accepting _fast_, _good_, and _best_
* Fixed; pre-multiply `putImageData()` components
* Fixed; `PixelArray` data is not premultiplied
* Added `CanvasRenderingContext2d#patternQuality` accessor, accepting _fast_, _good_, and _best_
* Fixed; pre-multiply `putImageData()` components
* Fixed; `PixelArray` data is not premultiplied
0.3.2 / 2010-11-26
==================
* Added --profile option to config
* Fixed `eio_custom` segfault(s). Closes #46
* Fixed two named colors. Closes #62 [thanks noonat]
* Fixed a few warnings
* Fixed; freeing data in `Image::loadJPEG()` on failure
* Fixed; include _jpeglib_ only when __HAVE_JPEG__
* Fixed; using `strstr()` instead of `strnstr()`
* Added --profile option to config
* Fixed `eio_custom` segfault(s). Closes #46
* Fixed two named colors. Closes #62 [thanks noonat]
* Fixed a few warnings
* Fixed; freeing data in `Image::loadJPEG()` on failure
* Fixed; include _jpeglib_ only when __HAVE_JPEG__
* Fixed; using `strstr()` instead of `strnstr()`
0.3.1 / 2010-11-24
==================
* Fixed; `Image` loading is sync until race-condition is resolved
* Fixed; `Image::loadJPEG()` return status based on errno
* Fixed; `Image` loading is sync until race-condition is resolved
* Fixed; `Image::loadJPEG()` return status based on errno
0.3.0 / 2010-11-24
==================
* Added arcTo(). Closes #11
* Added c color parser, _./examples/ray.js_ is now twice as fast
* Fixed `putImageData()` bug messing up rgba channels
* Added arcTo(). Closes #11
* Added c color parser, _./examples/ray.js_ is now twice as fast
* Fixed `putImageData()` bug messing up rgba channels
0.2.1 / 2010-11-19
==================
* Added image _resize_ example
* Fixed canvas resizing via `{width,height}=`. Closes #57
* Fixed `Canvas#getContext()`, caching the CanvasRenderingContext
* Fixed async image loading (test server still messed)
* Added image _resize_ example
* Fixed canvas resizing via `{width,height}=`. Closes #57
* Fixed `Canvas#getContext()`, caching the CanvasRenderingContext
* Fixed async image loading (test server still messed)
0.2.0 / 2010-11-18
==================
* Added jpeg `Image` support (when libjpeg is available)
* Added _hsl_ / _hsla_ color support. [Tom Carden]
* Added jpeg `Image` support (when libjpeg is available)
* Added _hsl_ / _hsla_ color support. [Tom Carden]
0.1.0 / 2010-11-17
==================
* Added `Image`
* Added `ImageData`
* Added `PixelArray`
* Added `CanvasRenderingContext2d#drawImage()`
* Added `CanvasRenderingContext2d#getImageData()`
* Added `CanvasRenderingContext2d#createImageData()`
* Added kraken blur benchmark example
* Added several new tests
* Fixed instanceof checks for many c++ methods
* Fixed test runner in firefox [Don Park]
* Added `Image`
* Added `ImageData`
* Added `PixelArray`
* Added `CanvasRenderingContext2d#drawImage()`
* Added `CanvasRenderingContext2d#getImageData()`
* Added `CanvasRenderingContext2d#createImageData()`
* Added kraken blur benchmark example
* Added several new tests
* Fixed instanceof checks for many c++ methods
* Fixed test runner in firefox [Don Park]
0.0.8 / 2010-11-12
==================
* Added `CanvasRenderingContext2d#drawImage()`
* Fixed `free()` call missing stdlib
* Fixed Image#{width,height} initialization to 0
* Fixed; load image on non-LOADING state
* Added `CanvasRenderingContext2d#drawImage()`
* Fixed `free()` call missing stdlib
* Fixed Image#{width,height} initialization to 0
* Fixed; load image on non-LOADING state
0.0.7 / 2010-11-12
==================
* Fixed _lighter_ for older versions of cairo
* Fixed _lighter_ for older versions of cairo
0.0.6 / 2010-11-12
==================
* Added `Image`
* Added conditional support for cairo 1.10.0 operators
* Added `Image`
* Added conditional support for cairo 1.10.0 operators
0.0.5 / 2010-11-10
==================
* Added custom port support to _test/server.js_
* Added more global composite operator support
* Added `Context2d#antialias=`
* Added _voronoi_ example
* Added -D__NDEBUG__ to default build
* Added __BUFFER_DATA__ macro for backwards compat buffer data access [Don Park]
* Fixed getter bug preventing patterns from being returned via `fillStyle` etc
* Added custom port support to _test/server.js_
* Added more global composite operator support
* Added `Context2d#antialias=`
* Added _voronoi_ example
* Added -D__NDEBUG__ to default build
* Added __BUFFER_DATA__ macro for backwards compat buffer data access [Don Park]
* Fixed getter bug preventing patterns from being returned via `fillStyle` etc
* Fixed; __CAIRO_STATUS_NO_MEMORY___ on failed {re,m}alloc()
* Fixed; free `Canvas::ToBuffer()` closure data
* Fixed; __CAIRO_STATUS_NO_MEMORY___ on failed {re,m}alloc()
* Fixed; free `Canvas::ToBuffer()` closure data
0.0.4 / 2010-11-09
==================
* Bump to fix npm engine cache bug...
* Bump to fix npm engine cache bug...
0.0.3 / 2010-11-09
==================
* Added async `toDataURL()` support
* Added async `toBuffer()` support
* Removed buffer utils
* Added async `toDataURL()` support
* Added async `toBuffer()` support
* Removed buffer utils
0.0.2 / 2010-11-08
==================
* Added shadow support (faster/better gaussian blur to come)
* Added node v0.3 support [Don Park]
* Added -O3 to build
* Removed `Canvas#savePNG()` use `Canvas#createPNGStream()`
* Added shadow support (faster/better gaussian blur to come)
* Added node v0.3 support [Don Park]
* Added -O3 to build
* Removed `Canvas#savePNG()` use `Canvas#createPNGStream()`
0.0.1 / 2010-11-04
==================
* Initial release
* Initial release

82
Readme.md

@ -1,20 +1,38 @@
[![NPM version](https://badge.fury.io/js/canvas.png)](http://badge.fury.io/js/canvas)
[![Dependency Status](https://gemnasium.com/LearnBoost/node-canvas.png)](https://gemnasium.com/LearnBoost/node-canvas)
node-canvas
===========
### Canvas graphics API backed by Cairo
[![Build Status](https://travis-ci.org/Automattic/node-canvas.svg?branch=master)](https://travis-ci.org/Automattic/node-canvas)
[![NPM version](https://badge.fury.io/js/canvas.svg)](http://badge.fury.io/js/canvas)
# node-canvas
Node canvas is a [Cairo](http://cairographics.org/) backed Canvas implementation for [NodeJS](http://nodejs.org).
node-canvas is a [Cairo](http://cairographics.org/) backed Canvas implementation for [NodeJS](http://nodejs.org).
## Authors
- TJ Holowaychuk ([visionmedia](http://github.com/visionmedia))
- Nathan Rajlich ([TooTallNate](http://github.com/TooTallNate))
- Rod Vagg ([rvagg](http://github.com/rvagg))
- Juriy Zaytsev ([kangax](http://github.com/kangax))
## Installation
$ npm install canvas
```bash
$ npm install canvas
```
Unless previously installed you'll _need_ __Cairo__. For system-specific installation view the [Wiki](https://github.com/LearnBoost/node-canvas/wiki/_pages).
You can quickly install Cairo and its dependencies for OS X using the one liner below:
```bash
$ wget https://raw.githubusercontent.com/LearnBoost/node-canvas/master/install -O - | sh
```
or if you use MacPorts
```bash
sudo port install pkgconfig libpng giflib freetype libpixman cairo
```
## Screencasts
- [Introduction](http://screenr.com/CTk)
@ -23,6 +41,7 @@ Unless previously installed you'll _need_ __Cairo__. For system-specific install
```javascript
var Canvas = require('canvas')
, Image = Canvas.Image
, canvas = new Canvas(200,200)
, ctx = canvas.getContext('2d');
@ -238,6 +257,17 @@ ctx.fillText('Hello World 3', 50, 80);
ctx.addPage();
```
## SVG support
Just like PDF support, make sure to install cairo with `--enable-svg=yes`.
You also need to tell node-canvas that it is working on SVG upon its initialization:
```js
var canvas = new Canvas(200, 500, 'svg');
// Use the normal primitives.
fs.writeFile('out.svg', canvas.toBuffer());
```
## Benchmarks
Although node-canvas is extremely new, and we have not even begun optimization yet it is already quite fast. For benchmarks vs other node canvas implementations view this [gist](https://gist.github.com/664922), or update the submodules and run `$ make benchmark` yourself.
@ -258,7 +288,7 @@ If you have not previously, init git submodules:
Build node-canvas:
$ node-waf configure build
$ node-gyp rebuild
Unit tests:
@ -278,45 +308,13 @@ Tested with and designed for:
For node 0.2.x `node-canvas` <= 0.4.3 may be used,
0.5.0 and above are designed for node 0.4.x only.
## Contributors
```
project : node-canvas
repo age : 1 year, 11 months
active : 120 days
commits : 963
files : 72
authors :
816 Tj Holowaychuk 84.7%
58 TJ Holowaychuk 6.0%
23 c-spencer 2.4%
16 Nathan Rajlich 1.7%
12 atomizer 1.2%
6 Elijah Hamovitz 0.6%
5 Luigi Pinca 0.5%
5 Robert Sköld 0.5%
4 obarthel 0.4%
3 Don Park 0.3%
2 Andreas Botsikas 0.2%
2 Gabriel Falcao 0.2%
1 Brian McKinney 0.1%
1 Seiya Konno 0.1%
1 Syoyo Fujita 0.1%
1 Marcello Bastea-Forte 0.1%
1 Tharit 0.1%
1 Konstantin Käfer 0.1%
1 Tom Carden 0.1%
1 Walt Lin 0.1%
1 David Björklund 0.1%
1 Brian White 0.1%
1 Philippe Plantier 0.1%
```
## License
(The MIT License)
Copyright (c) 2010 LearnBoost &lt;dev@learnboost.ca&gt;
Copyright (c) 2010 LearnBoost, and contributors &lt;dev@learnboost.com&gt;
Copyright (c) 2014 Automattic, Inc and contributors &lt;dev@automattic.com&gt;
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

41
binding.gyp

@ -19,6 +19,25 @@
}]
],
'targets': [
{
'target_name': 'canvas-postbuild',
'dependencies': ['canvas'],
'conditions': [
['OS=="win"', {
'copies': [{
'destination': '<(PRODUCT_DIR)',
'files': [
'<(GTK_Root)/bin/libcairo-2.dll',
'<(GTK_Root)/bin/libexpat-1.dll',
'<(GTK_Root)/bin/libfontconfig-1.dll',
'<(GTK_Root)/bin/libfreetype-6.dll',
'<(GTK_Root)/bin/libpng14-14.dll',
'<(GTK_Root)/bin/zlib1.dll',
]
}]
}]
]
},
{
'target_name': 'canvas',
'include_dirs': ["<!(node -e \"require('nan')\")"],
@ -45,7 +64,27 @@
'defines': [
'snprintf=_snprintf',
'_USE_MATH_DEFINES' # for M_PI
]
],
'configurations': {
'Debug': {
'msvs_settings': {
'VCCLCompilerTool': {
'WarningLevel': 4,
'ExceptionHandling': 1,
'DisableSpecificWarnings': [4100, 4127, 4201, 4244, 4267, 4506, 4611, 4714]
}
}
},
'Release': {
'msvs_settings': {
'VCCLCompilerTool': {
'WarningLevel': 4,
'ExceptionHandling': 1,
'DisableSpecificWarnings': [4100, 4127, 4201, 4244, 4267, 4506, 4611, 4714]
}
}
}
}
}, { # 'OS!="win"'
'libraries': [
'<!@(pkg-config pixman-1 --libs)',

22
examples/small-svg.js

@ -0,0 +1,22 @@
var Canvas = require('../')
, canvas = new Canvas(400, 200, 'svg')
, ctx = canvas.getContext('2d')
, fs = require('fs');
var y = 80
, x = 50;
ctx.font = '22px Helvetica';
ctx.fillText('node-canvas SVG', x, y);
ctx.font = '10px Arial';
ctx.fillText('Just a quick example of SVGs with node-canvas', x, y += 20);
ctx.globalAlpha = .5;
ctx.fillRect(x, y += 20, 10, 10);
ctx.fillRect(x += 20, y, 10, 10);
ctx.fillRect(x += 20, y, 10, 10);
fs.writeFile('out.svg', canvas.toBuffer());
console.log('created out.svg');

14
install

@ -1,10 +1,11 @@
#!/bin/sh
PKG_CONFIG="http://pkgconfig.freedesktop.org/releases/pkg-config-0.23.tar.gz"
PIXMAN="http://www.cairographics.org/releases/pixman-0.28.0.tar.gz"
CAIRO="http://cairographics.org/releases/cairo-1.12.8.tar.xz"
PKG_CONFIG="http://pkgconfig.freedesktop.org/releases/pkg-config-0.28.tar.gz"
PIXMAN="http://www.cairographics.org/releases/pixman-0.32.4.tar.gz"
CAIRO="http://cairographics.org/releases/cairo-1.12.16.tar.xz"
FREETYPE="http://download.savannah.gnu.org/releases/freetype/freetype-2.4.10.tar.gz"
LIBPNG="ftp://ftp.simplesystems.org/pub/libpng/png/src/libpng-1.5.13.tar.gz"
LIBPNG="http://downloads.sourceforge.net/project/libpng/libpng16/1.6.10/libpng-1.6.10.tar.gz"
GIF_LIB="https://downloads.sourceforge.net/project/giflib/giflib-4.x/giflib-4.1.6/giflib-4.1.6.tar.gz"
PREFIX=${1-/usr/local}
require() {
@ -20,7 +21,7 @@ require() {
fetch() {
local tarball=`basename $1`
echo "... downloading $tarball"
local dir=${tarball/.tar.gz/}
local dir=`basename $tarball .tar.gz`
curl -# -L $1 -o $tarball \
&& echo "... unpacking" \
&& tar -zxf $tarball \
@ -32,7 +33,7 @@ fetch() {
fetch_xz() {
local tarball=`basename $1`
echo "... downloading $tarball"
local dir=${tarball/.tar.xz/}
local dir=`basename $tarball .tar.xz`
curl -# -L $1 -o $tarball \
&& echo "... unpacking" \
&& tar -xJf $tarball \
@ -58,6 +59,7 @@ require tar
test `which pkg-config` || fetch $PKG_CONFIG
require 'pkg-config'
fetch $LIBPNG
fetch $GIF_LIB
fetch $FREETYPE
fetch $PIXMAN
fetch_xz $CAIRO

2
lib/canvas.js

@ -13,7 +13,7 @@ var canvas = require('./bindings')
, Canvas = canvas.Canvas
, Image = canvas.Image
, cairoVersion = canvas.cairoVersion
, PixelArray = canvas.PixelArray
, PixelArray = canvas.CanvasPixelArray
, Context2d = require('./context2d')
, PNGStream = require('./pngstream')
, JPEGStream = require('./jpegstream')

64
package.json

@ -1,27 +1,39 @@
{ "name": "canvas"
, "description": "Canvas graphics API backed by Cairo"
, "version": "1.1.3"
, "author": "TJ Holowaychuk <tj@learnboost.com>"
, "contributors": [
"Nathan Rajlich <nathan@tootallnate.net>"
, "Rod Vagg <r@va.gg>"
, "Juriy Zaytsev <kangax@gmail.com>"
]
, "keywords": ["canvas", "graphic", "graphics", "pixman", "cairo", "image", "images", "pdf"]
, "homepage": "https://github.com/learnboost/node-canvas"
, "repository": "git://github.com/learnboost/node-canvas"
, "scripts": {
"test": "make test"
}
, "dependencies": {
"nan": "~0.7.0"
}
, "devDependencies": {
"express": "3.0"
, "jade": "0.28.1"
, "mocha": "*"
, "should": "*"
}
, "engines": { "node": ">= 0.6.0" }
, "main": "./lib/canvas.js"
{
"name": "canvas",
"description": "Canvas graphics API backed by Cairo",
"version": "1.1.6",
"author": "TJ Holowaychuk <tj@learnboost.com>",
"contributors": [
"Nathan Rajlich <nathan@tootallnate.net>",
"Rod Vagg <r@va.gg>",
"Juriy Zaytsev <kangax@gmail.com>"
],
"keywords": [
"canvas",
"graphic",
"graphics",
"pixman",
"cairo",
"image",
"images",
"pdf"
],
"homepage": "https://github.com/learnboost/node-canvas",
"repository": "git://github.com/learnboost/node-canvas",
"scripts": {
"test": "make test"
},
"dependencies": {
"nan": "~1.2.0"
},
"devDependencies": {
"express": "3.0",
"jade": "0.28.1",
"mocha": "*",
"should": "*"
},
"engines": {
"node": ">= 0.6.0"
},
"main": "./lib/canvas.js"
}

117
src/Canvas.cc

@ -13,6 +13,7 @@
#include <node_buffer.h>
#include <node_version.h>
#include <cairo/cairo-pdf.h>
#include <cairo/cairo-svg.h>
#include "closure.h"
#ifdef HAVE_JPEG
@ -30,10 +31,10 @@ Canvas::Initialize(Handle<Object> target) {
NanScope();
// Constructor
Local<FunctionTemplate> ctor = FunctionTemplate::New(Canvas::New);
NanAssignPersistent(FunctionTemplate, constructor, ctor);
Local<FunctionTemplate> ctor = NanNew<FunctionTemplate>(Canvas::New);
NanAssignPersistent(constructor, ctor);
ctor->InstanceTemplate()->SetInternalFieldCount(1);
ctor->SetClassName(NanSymbol("Canvas"));
ctor->SetClassName(NanNew("Canvas"));
// Prototype
Local<ObjectTemplate> proto = ctor->PrototypeTemplate();
@ -42,19 +43,19 @@ Canvas::Initialize(Handle<Object> target) {
#ifdef HAVE_JPEG
NODE_SET_PROTOTYPE_METHOD(ctor, "streamJPEGSync", StreamJPEGSync);
#endif
proto->SetAccessor(NanSymbol("type"), GetType);
proto->SetAccessor(NanSymbol("width"), GetWidth, SetWidth);
proto->SetAccessor(NanSymbol("height"), GetHeight, SetHeight);
proto->Set("PNG_NO_FILTERS", Uint32::New(PNG_NO_FILTERS));
proto->Set("PNG_FILTER_NONE", Uint32::New(PNG_FILTER_NONE));
proto->Set("PNG_FILTER_SUB", Uint32::New(PNG_FILTER_SUB));
proto->Set("PNG_FILTER_UP", Uint32::New(PNG_FILTER_UP));
proto->Set("PNG_FILTER_AVG", Uint32::New(PNG_FILTER_AVG));
proto->Set("PNG_FILTER_PAETH", Uint32::New(PNG_FILTER_PAETH));
proto->Set("PNG_ALL_FILTERS", Uint32::New(PNG_ALL_FILTERS));
target->Set(NanSymbol("Canvas"), ctor->GetFunction());
proto->SetAccessor(NanNew("type"), GetType);
proto->SetAccessor(NanNew("width"), GetWidth, SetWidth);
proto->SetAccessor(NanNew("height"), GetHeight, SetHeight);
NanSetTemplate(proto, "PNG_NO_FILTERS", NanNew<Uint32>(PNG_NO_FILTERS));
NanSetTemplate(proto, "PNG_FILTER_NONE", NanNew<Uint32>(PNG_FILTER_NONE));
NanSetTemplate(proto, "PNG_FILTER_SUB", NanNew<Uint32>(PNG_FILTER_SUB));
NanSetTemplate(proto, "PNG_FILTER_UP", NanNew<Uint32>(PNG_FILTER_UP));
NanSetTemplate(proto, "PNG_FILTER_AVG", NanNew<Uint32>(PNG_FILTER_AVG));
NanSetTemplate(proto, "PNG_FILTER_PAETH", NanNew<Uint32>(PNG_FILTER_PAETH));
NanSetTemplate(proto, "PNG_ALL_FILTERS", NanNew<Uint32>(PNG_ALL_FILTERS));
target->Set(NanNew("Canvas"), ctor->GetFunction());
}
/*
@ -67,9 +68,11 @@ NAN_METHOD(Canvas::New) {
canvas_type_t type = CANVAS_TYPE_IMAGE;
if (args[0]->IsNumber()) width = args[0]->Uint32Value();
if (args[1]->IsNumber()) height = args[1]->Uint32Value();
if (args[2]->IsString()) type = !strcmp("pdf", *String::AsciiValue(args[2]))
if (args[2]->IsString()) type = !strcmp("pdf", *String::Utf8Value(args[2]))
? CANVAS_TYPE_PDF
: CANVAS_TYPE_IMAGE;
: !strcmp("svg", *String::Utf8Value(args[2]))
? CANVAS_TYPE_SVG
: CANVAS_TYPE_IMAGE;
Canvas *canvas = new Canvas(width, height, type);
canvas->Wrap(args.This());
NanReturnValue(args.This());
@ -82,7 +85,7 @@ NAN_METHOD(Canvas::New) {
NAN_GETTER(Canvas::GetType) {
NanScope();
Canvas *canvas = ObjectWrap::Unwrap<Canvas>(args.This());
NanReturnValue(String::New(canvas->isPDF() ? "pdf" : "image"));
NanReturnValue(NanNew<String>(canvas->isPDF() ? "pdf" : canvas->isSVG() ? "svg" : "image"));
}
/*
@ -92,7 +95,7 @@ NAN_GETTER(Canvas::GetType) {
NAN_GETTER(Canvas::GetWidth) {
NanScope();
Canvas *canvas = ObjectWrap::Unwrap<Canvas>(args.This());
NanReturnValue(Number::New(canvas->width));
NanReturnValue(NanNew<Number>(canvas->width));
}
/*
@ -115,7 +118,7 @@ NAN_SETTER(Canvas::SetWidth) {
NAN_GETTER(Canvas::GetHeight) {
NanScope();
Canvas *canvas = ObjectWrap::Unwrap<Canvas>(args.This());
NanReturnValue(Number::New(canvas->height));
NanReturnValue(NanNew<Number>(canvas->height));
}
/*
@ -211,7 +214,7 @@ Canvas::EIO_AfterToBuffer(eio_req *req) {
} else {
Local<Object> buf = NanNewBufferHandle((char*)closure->data, closure->len);
memcpy(Buffer::Data(buf), closure->data, closure->len);
Local<Value> argv[2] = { NanNewLocal<Value>(Null()), buf };
Local<Value> argv[2] = { NanNew(NanNull()), buf };
closure->pfn->Call(2, argv);
}
@ -238,7 +241,7 @@ NAN_METHOD(Canvas::ToBuffer) {
Canvas *canvas = ObjectWrap::Unwrap<Canvas>(args.This());
// TODO: async / move this out
if (canvas->isPDF()) {
if (canvas->isPDF() || canvas->isSVG()) {
cairo_surface_finish(canvas->surface());
closure_t *closure = (closure_t *) canvas->closure();
@ -246,13 +249,13 @@ NAN_METHOD(Canvas::ToBuffer) {
NanReturnValue(buf);
}
if (args.Length() > 1 && !(args[1]->StrictEquals(Undefined()) && args[2]->StrictEquals(Undefined()))) {
if (!args[1]->StrictEquals(Undefined())) {
if (args.Length() > 1 && !(args[1]->StrictEquals(NanUndefined()) && args[2]->StrictEquals(NanUndefined()))) {
if (!args[1]->StrictEquals(NanUndefined())) {
bool good = true;
if (args[1]->IsNumber()) {
compression_level = args[1]->Uint32Value();
} else if (args[1]->IsString()) {
if (args[1]->StrictEquals(String::New("0"))) {
if (args[1]->StrictEquals(NanNew<String>("0"))) {
compression_level = 0;
} else {
uint32_t tmp = args[1]->Uint32Value();
@ -275,7 +278,7 @@ NAN_METHOD(Canvas::ToBuffer) {
}
}
if (!args[2]->StrictEquals(Undefined())) {
if (!args[2]->StrictEquals(NanUndefined())) {
if (args[2]->IsUint32()) {
filter = args[2]->Uint32Value();
} else {
@ -348,10 +351,10 @@ streamPNG(void *c, const uint8_t *data, unsigned len) {
closure_t *closure = (closure_t *) c;
Local<Object> buf = NanNewBufferHandle((char *)data, len);
Local<Value> argv[3] = {
NanNewLocal<Value>(Null())
NanNew(NanNull())
, buf
, Integer::New(len) };
MakeCallback(Context::GetCurrent()->Global(), closure->fn, 3, argv);
, NanNew<Integer>(len) };
NanMakeCallback(NanGetCurrentContext()->Global(), closure->fn, 3, argv);
return CAIRO_STATUS_SUCCESS;
}
@ -367,13 +370,13 @@ NAN_METHOD(Canvas::StreamPNGSync) {
if (!args[0]->IsFunction())
return NanThrowTypeError("callback function required");
if (args.Length() > 1 && !(args[1]->StrictEquals(Undefined()) && args[2]->StrictEquals(Undefined()))) {
if (!args[1]->StrictEquals(Undefined())) {
if (args.Length() > 1 && !(args[1]->StrictEquals(NanUndefined()) && args[2]->StrictEquals(NanUndefined()))) {
if (!args[1]->StrictEquals(NanUndefined())) {
bool good = true;
if (args[1]->IsNumber()) {
compression_level = args[1]->Uint32Value();
} else if (args[1]->IsString()) {
if (args[1]->StrictEquals(String::New("0"))) {
if (args[1]->StrictEquals(NanNew<String>("0"))) {
compression_level = 0;
} else {
uint32_t tmp = args[1]->Uint32Value();
@ -396,7 +399,7 @@ NAN_METHOD(Canvas::StreamPNGSync) {
}
}
if (!args[2]->StrictEquals(Undefined())) {
if (!args[2]->StrictEquals(NanUndefined())) {
if (args[2]->IsUint32()) {
filter = args[1]->Uint32Value();
} else {
@ -420,13 +423,13 @@ NAN_METHOD(Canvas::StreamPNGSync) {
NanReturnValue(try_catch.ReThrow());
} else if (status) {
Local<Value> argv[1] = { Canvas::Error(status) };
MakeCallback(Context::GetCurrent()->Global(), closure.fn, 1, argv);
NanMakeCallback(NanGetCurrentContext()->Global(), closure.fn, 1, argv);
} else {
Local<Value> argv[3] = {
NanNewLocal<Value>(Null())
, NanNewLocal<Value>(Null())
, Integer::New(0) };
MakeCallback(Context::GetCurrent()->Global(), closure.fn, 3, argv);
NanNew(NanNull())
, NanNew(NanNull())
, NanNew<Uint32>(0) };
NanMakeCallback(NanGetCurrentContext()->Global(), closure.fn, 1, argv);
}
NanReturnUndefined();
}
@ -480,10 +483,16 @@ Canvas::Canvas(int w, int h, canvas_type_t t): ObjectWrap() {
cairo_status_t status = closure_init((closure_t *) _closure, this, 0, PNG_NO_FILTERS);
assert(status == CAIRO_STATUS_SUCCESS);
_surface = cairo_pdf_surface_create_for_stream(toBuffer, _closure, w, h);
} else if (CANVAS_TYPE_SVG == t) {
_closure = malloc(sizeof(closure_t));
assert(_closure);
cairo_status_t status = closure_init((closure_t *) _closure, this, 0, PNG_NO_FILTERS);
assert(status == CAIRO_STATUS_SUCCESS);
_surface = cairo_svg_surface_create_for_stream(toBuffer, _closure, w, h);
} else {
_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h);
assert(_surface);
V8::AdjustAmountOfExternalAllocatedMemory(4 * w * h);
NanAdjustExternalMemory(4 * w * h);
}
}
@ -494,6 +503,7 @@ Canvas::Canvas(int w, int h, canvas_type_t t): ObjectWrap() {
Canvas::~Canvas() {
switch (type) {
case CANVAS_TYPE_PDF:
case CANVAS_TYPE_SVG:
cairo_surface_finish(_surface);
closure_destroy((closure_t *) _closure);
free(_closure);
@ -501,7 +511,7 @@ Canvas::~Canvas() {
break;
case CANVAS_TYPE_IMAGE:
cairo_surface_destroy(_surface);
V8::AdjustAmountOfExternalAllocatedMemory(-4 * width * height);
NanAdjustExternalMemory(-4 * width * height);
break;
}
}
@ -512,20 +522,39 @@ Canvas::~Canvas() {
void
Canvas::resurface(Handle<Object> canvas) {
NanScope();
Handle<Value> context;
switch (type) {
case CANVAS_TYPE_PDF:
cairo_pdf_surface_set_size(_surface, width, height);
break;
case CANVAS_TYPE_SVG:
// Re-surface
cairo_surface_finish(_surface);
closure_destroy((closure_t *) _closure);
cairo_surface_destroy(_surface);
closure_init((closure_t *) _closure, this, 0, PNG_NO_FILTERS);
_surface = cairo_svg_surface_create_for_stream(toBuffer, _closure, width, height);
// Reset context
context = canvas->Get(NanNew<String>("context"));
if (!context->IsUndefined()) {
Context2d *context2d = ObjectWrap::Unwrap<Context2d>(context->ToObject());
cairo_t *prev = context2d->context();
context2d->setContext(cairo_create(surface()));
cairo_destroy(prev);
}
break;
case CANVAS_TYPE_IMAGE:
// Re-surface
int old_width = cairo_image_surface_get_width(_surface);
int old_height = cairo_image_surface_get_height(_surface);
cairo_surface_destroy(_surface);
_surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
V8::AdjustAmountOfExternalAllocatedMemory(4 * (width * height - old_width * old_height));
NanAdjustExternalMemory(4 * (width * height - old_width * old_height));
// Reset context
Handle<Value> context = canvas->Get(String::New("context"));
context = canvas->Get(NanNew<String>("context"));
if (!context->IsUndefined()) {
Context2d *context2d = ObjectWrap::Unwrap<Context2d>(context->ToObject());
cairo_t *prev = context2d->context();
@ -542,5 +571,5 @@ Canvas::resurface(Handle<Object> canvas) {
Local<Value>
Canvas::Error(cairo_status_t status) {
return Exception::Error(String::New(cairo_status_to_string(status)));
return Exception::Error(NanNew<String>(cairo_status_to_string(status)));
}

6
src/Canvas.h

@ -19,7 +19,7 @@
#include <cairo/cairo.h>
#endif
#include "nan.h"
#include <nan.h>
using namespace v8;
using namespace node;
@ -39,7 +39,8 @@ using namespace node;
typedef enum {
CANVAS_TYPE_IMAGE,
CANVAS_TYPE_PDF
CANVAS_TYPE_PDF,
CANVAS_TYPE_SVG
} canvas_type_t;
/*
@ -78,6 +79,7 @@ class Canvas: public node::ObjectWrap {
#endif
inline bool isPDF(){ return CANVAS_TYPE_PDF == type; }
inline bool isSVG(){ return CANVAS_TYPE_SVG == type; }
inline cairo_surface_t *surface(){ return _surface; }
inline void *closure(){ return _closure; }
inline uint8_t *data(){ return cairo_image_surface_get_data(_surface); }

12
src/CanvasGradient.cc

@ -20,14 +20,14 @@ Gradient::Initialize(Handle<Object> target) {
NanScope();
// Constructor
Local<FunctionTemplate> ctor = FunctionTemplate::New(Gradient::New);
NanAssignPersistent(FunctionTemplate, constructor, ctor);
Local<FunctionTemplate> ctor = NanNew<FunctionTemplate>(Gradient::New);
NanAssignPersistent(constructor, ctor);
ctor->InstanceTemplate()->SetInternalFieldCount(1);
ctor->SetClassName(NanSymbol("CanvasGradient"));
ctor->SetClassName(NanNew("CanvasGradient"));
// Prototype
NODE_SET_PROTOTYPE_METHOD(ctor, "addColorStop", AddColorStop);
target->Set(NanSymbol("CanvasGradient"), ctor->GetFunction());
target->Set(NanNew("CanvasGradient"), ctor->GetFunction());
}
/*
@ -60,7 +60,7 @@ NAN_METHOD(Gradient::New) {
grad->Wrap(args.This());
NanReturnValue(args.This());
}
return NanThrowTypeError("invalid arguments");
}
@ -77,7 +77,7 @@ NAN_METHOD(Gradient::AddColorStop) {
Gradient *grad = ObjectWrap::Unwrap<Gradient>(args.This());
short ok;
String::AsciiValue str(args[1]);
String::Utf8Value str(args[1]);
uint32_t rgba = rgba_from_string(*str, &ok);
if (ok) {

1
src/CanvasGradient.h

@ -8,7 +8,6 @@
#ifndef __NODE_GRADIENT_H__
#define __NODE_GRADIENT_H__
#include "nan.h"
#include "Canvas.h"
class Gradient: public node::ObjectWrap {

10
src/CanvasPattern.cc

@ -20,16 +20,16 @@ Pattern::Initialize(Handle<Object> target) {
NanScope();
// Constructor
Local<FunctionTemplate> ctor = FunctionTemplate::New(Pattern::New);
NanAssignPersistent(FunctionTemplate, constructor, ctor);
Local<FunctionTemplate> ctor = NanNew<FunctionTemplate>(Pattern::New);
NanAssignPersistent(constructor, ctor);
ctor->InstanceTemplate()->SetInternalFieldCount(1);
ctor->SetClassName(NanSymbol("CanvasPattern"));
ctor->SetClassName(NanNew("CanvasPattern"));
ctor->InstanceTemplate()->SetInternalFieldCount(1);
ctor->SetClassName(NanSymbol("CanvasPattern"));
ctor->SetClassName(NanNew("CanvasPattern"));
// Prototype
target->Set(NanSymbol("CanvasPattern"), ctor->GetFunction());
target->Set(NanNew("CanvasPattern"), ctor->GetFunction());
}
/*

1
src/CanvasPattern.h

@ -8,7 +8,6 @@
#ifndef __NODE_PATTERN_H__
#define __NODE_PATTERN_H__
#include "nan.h"
#include "Canvas.h"
class Pattern: public node::ObjectWrap {

269
src/CanvasRenderingContext2d.cc

@ -8,6 +8,8 @@
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <vector>
#include <algorithm>
#include "Canvas.h"
#include "Point.h"
#include "Image.h"
@ -20,6 +22,12 @@
#include "FontFace.h"
#endif
// Windows doesn't support the C99 names for these
#ifndef isnan
#define isnan(x) _isnan(x)
#define isinf(x) (!_finite(x))
#endif
Persistent<FunctionTemplate> Context2d::constructor;
/*
@ -81,10 +89,10 @@ Context2d::Initialize(Handle<Object> target) {
NanScope();
// Constructor
Local<FunctionTemplate> ctor = FunctionTemplate::New(Context2d::New);
NanAssignPersistent(FunctionTemplate, constructor, ctor);
Local<FunctionTemplate> ctor = NanNew<FunctionTemplate>(Context2d::New);
NanAssignPersistent(constructor, ctor);
ctor->InstanceTemplate()->SetInternalFieldCount(1);
ctor->SetClassName(NanSymbol("CanvasRenderingContext2d"));
ctor->SetClassName(NanNew("CanvasRenderingContext2d"));
// Prototype
Local<ObjectTemplate> proto = ctor->PrototypeTemplate();
@ -117,6 +125,8 @@ Context2d::Initialize(Handle<Object> target) {
NODE_SET_PROTOTYPE_METHOD(ctor, "closePath", ClosePath);
NODE_SET_PROTOTYPE_METHOD(ctor, "arc", Arc);
NODE_SET_PROTOTYPE_METHOD(ctor, "arcTo", ArcTo);
NODE_SET_PROTOTYPE_METHOD(ctor, "setLineDash", SetLineDash);
NODE_SET_PROTOTYPE_METHOD(ctor, "getLineDash", GetLineDash);
NODE_SET_PROTOTYPE_METHOD(ctor, "_setFont", SetFont);
#ifdef HAVE_FREETYPE
NODE_SET_PROTOTYPE_METHOD(ctor, "_setFontFace", SetFontFace);
@ -127,23 +137,24 @@ Context2d::Initialize(Handle<Object> target) {
NODE_SET_PROTOTYPE_METHOD(ctor, "_setStrokePattern", SetStrokePattern);
NODE_SET_PROTOTYPE_METHOD(ctor, "_setTextBaseline", SetTextBaseline);
NODE_SET_PROTOTYPE_METHOD(ctor, "_setTextAlignment", SetTextAlignment);
proto->SetAccessor(NanSymbol("patternQuality"), GetPatternQuality, SetPatternQuality);
proto->SetAccessor(NanSymbol("globalCompositeOperation"), GetGlobalCompositeOperation, SetGlobalCompositeOperation);
proto->SetAccessor(NanSymbol("globalAlpha"), GetGlobalAlpha, SetGlobalAlpha);
proto->SetAccessor(NanSymbol("shadowColor"), GetShadowColor, SetShadowColor);
proto->SetAccessor(NanSymbol("fillColor"), GetFillColor);
proto->SetAccessor(NanSymbol("strokeColor"), GetStrokeColor);
proto->SetAccessor(NanSymbol("miterLimit"), GetMiterLimit, SetMiterLimit);
proto->SetAccessor(NanSymbol("lineWidth"), GetLineWidth, SetLineWidth);
proto->SetAccessor(NanSymbol("lineCap"), GetLineCap, SetLineCap);
proto->SetAccessor(NanSymbol("lineJoin"), GetLineJoin, SetLineJoin);
proto->SetAccessor(NanSymbol("shadowOffsetX"), GetShadowOffsetX, SetShadowOffsetX);
proto->SetAccessor(NanSymbol("shadowOffsetY"), GetShadowOffsetY, SetShadowOffsetY);
proto->SetAccessor(NanSymbol("shadowBlur"), GetShadowBlur, SetShadowBlur);
proto->SetAccessor(NanSymbol("antialias"), GetAntiAlias, SetAntiAlias);
proto->SetAccessor(NanSymbol("textDrawingMode"), GetTextDrawingMode, SetTextDrawingMode);
proto->SetAccessor(NanSymbol("filter"), GetFilter, SetFilter);
target->Set(NanSymbol("CanvasRenderingContext2d"), ctor->GetFunction());
proto->SetAccessor(NanNew("patternQuality"), GetPatternQuality, SetPatternQuality);
proto->SetAccessor(NanNew("globalCompositeOperation"), GetGlobalCompositeOperation, SetGlobalCompositeOperation);
proto->SetAccessor(NanNew("globalAlpha"), GetGlobalAlpha, SetGlobalAlpha);
proto->SetAccessor(NanNew("shadowColor"), GetShadowColor, SetShadowColor);
proto->SetAccessor(NanNew("fillColor"), GetFillColor);
proto->SetAccessor(NanNew("strokeColor"), GetStrokeColor);
proto->SetAccessor(NanNew("miterLimit"), GetMiterLimit, SetMiterLimit);
proto->SetAccessor(NanNew("lineWidth"), GetLineWidth, SetLineWidth);
proto->SetAccessor(NanNew("lineCap"), GetLineCap, SetLineCap);
proto->SetAccessor(NanNew("lineJoin"), GetLineJoin, SetLineJoin);
proto->SetAccessor(NanNew("lineDashOffset"), GetLineDashOffset, SetLineDashOffset);
proto->SetAccessor(NanNew("shadowOffsetX"), GetShadowOffsetX, SetShadowOffsetX);
proto->SetAccessor(NanNew("shadowOffsetY"), GetShadowOffsetY, SetShadowOffsetY);
proto->SetAccessor(NanNew("shadowBlur"), GetShadowBlur, SetShadowBlur);
proto->SetAccessor(NanNew("antialias"), GetAntiAlias, SetAntiAlias);
proto->SetAccessor(NanNew("textDrawingMode"), GetTextDrawingMode, SetTextDrawingMode);
proto->SetAccessor(NanNew("filter"), GetFilter, SetFilter);
target->Set(NanNew("CanvasRenderingContext2d"), ctor->GetFunction());
}
/*
@ -523,8 +534,8 @@ NAN_METHOD(Context2d::PutImageData) {
switch (args.Length()) {
// imageData, dx, dy
case 3:
cols = arr->width();
rows = arr->height();
cols = std::min(arr->width(), context->canvas()->width - dx);
rows = std::min(arr->height(), context->canvas()->height - dy);
break;
// imageData, dx, dy, sx, sy, sw, sh
case 7:
@ -536,16 +547,17 @@ NAN_METHOD(Context2d::PutImageData) {
if (sy < 0) sh += sy, sy = 0;
if (sx + sw > arr->width()) sw = arr->width() - sx;
if (sy + sh > arr->height()) sh = arr->height() - sy;
if (sw <= 0 || sh <= 0) NanReturnUndefined();
cols = sw;
rows = sh;
dx += sx;
dy += sy;
cols = std::min(sw, context->canvas()->width - dx);
rows = std::min(sh, context->canvas()->height - dy);
break;
default:
return NanThrowError("invalid arguments");
}
if (cols <= 0 || rows <= 0) NanReturnUndefined();
uint8_t *srcRows = src + sy * srcStride + sx * 4;
for (int y = 0; y < rows; ++y) {
uint32_t *row = (uint32_t *)(dst + dstStride * (y + dy));
@ -694,7 +706,7 @@ NAN_METHOD(Context2d::DrawImage) {
NAN_GETTER(Context2d::GetGlobalAlpha) {
NanScope();
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
NanReturnValue(Number::New(context->state->globalAlpha));
NanReturnValue(NanNew<Number>(context->state->globalAlpha));
}
/*
@ -728,7 +740,6 @@ NAN_GETTER(Context2d::GetGlobalCompositeOperation) {
case CAIRO_OPERATOR_DEST_IN: op = "destination-in"; break;
case CAIRO_OPERATOR_DEST_OUT: op = "destination-out"; break;
case CAIRO_OPERATOR_DEST_OVER: op = "destination-over"; break;
case CAIRO_OPERATOR_ADD: op = "lighter"; break;
case CAIRO_OPERATOR_CLEAR: op = "clear"; break;
case CAIRO_OPERATOR_SOURCE: op = "source"; break;
case CAIRO_OPERATOR_DEST: op = "dest"; break;
@ -738,6 +749,7 @@ NAN_GETTER(Context2d::GetGlobalCompositeOperation) {
// supported by resent versions of cairo
#if CAIRO_VERSION_MINOR >= 10
case CAIRO_OPERATOR_LIGHTEN: op = "lighten"; break;
case CAIRO_OPERATOR_ADD: op = "add"; break;
case CAIRO_OPERATOR_DARKEN: op = "darker"; break;
case CAIRO_OPERATOR_MULTIPLY: op = "multiply"; break;
case CAIRO_OPERATOR_SCREEN: op = "screen"; break;
@ -752,10 +764,12 @@ NAN_GETTER(Context2d::GetGlobalCompositeOperation) {
case CAIRO_OPERATOR_COLOR_BURN: op = "color-burn"; break;
case CAIRO_OPERATOR_DIFFERENCE: op = "difference"; break;
case CAIRO_OPERATOR_EXCLUSION: op = "exclusion"; break;
#else
case CAIRO_OPERATOR_ADD: op = "lighter"; break;
#endif
}
NanReturnValue(NanSymbol(op));
NanReturnValue(NanNew(op));
}
/*
@ -764,7 +778,7 @@ NAN_GETTER(Context2d::GetGlobalCompositeOperation) {
NAN_SETTER(Context2d::SetPatternQuality) {
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
String::AsciiValue quality(value->ToString());
String::Utf8Value quality(value->ToString());
if (0 == strcmp("fast", *quality)) {
context->state->patternQuality = CAIRO_FILTER_FAST;
} else if (0 == strcmp("good", *quality)) {
@ -793,7 +807,7 @@ NAN_GETTER(Context2d::GetPatternQuality) {
case CAIRO_FILTER_BILINEAR: quality = "bilinear"; break;
default: quality = "good";
}
NanReturnValue(NanSymbol(quality));
NanReturnValue(NanNew(quality));
}
/*
@ -803,7 +817,7 @@ NAN_GETTER(Context2d::GetPatternQuality) {
NAN_SETTER(Context2d::SetGlobalCompositeOperation) {
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
cairo_t *ctx = context->context();
String::AsciiValue type(value->ToString());
String::Utf8Value type(value->ToString());
if (0 == strcmp("xor", *type)) {
cairo_set_operator(ctx, CAIRO_OPERATOR_XOR);
} else if (0 == strcmp("source-atop", *type)) {
@ -833,6 +847,8 @@ NAN_SETTER(Context2d::SetGlobalCompositeOperation) {
// Non-standard
// supported by resent versions of cairo
#if CAIRO_VERSION_MINOR >= 10
} else if (0 == strcmp("add", *type)) {
cairo_set_operator(ctx, CAIRO_OPERATOR_ADD);
} else if (0 == strcmp("lighten", *type)) {
cairo_set_operator(ctx, CAIRO_OPERATOR_LIGHTEN);
} else if (0 == strcmp("darker", *type)) {
@ -878,7 +894,7 @@ NAN_SETTER(Context2d::SetGlobalCompositeOperation) {
NAN_GETTER(Context2d::GetShadowOffsetX) {
NanScope();
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
NanReturnValue(Number::New(context->state->shadowOffsetX));
NanReturnValue(NanNew<Number>(context->state->shadowOffsetX));
}
/*
@ -897,7 +913,7 @@ NAN_SETTER(Context2d::SetShadowOffsetX) {
NAN_GETTER(Context2d::GetShadowOffsetY) {
NanScope();
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
NanReturnValue(Number::New(context->state->shadowOffsetY));
NanReturnValue(NanNew<Number>(context->state->shadowOffsetY));
}
/*
@ -916,7 +932,7 @@ NAN_SETTER(Context2d::SetShadowOffsetY) {
NAN_GETTER(Context2d::GetShadowBlur) {
NanScope();
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
NanReturnValue(Number::New(context->state->shadowBlur));
NanReturnValue(NanNew<Number>(context->state->shadowBlur));
}
/*
@ -945,7 +961,7 @@ NAN_GETTER(Context2d::GetAntiAlias) {
case CAIRO_ANTIALIAS_SUBPIXEL: aa = "subpixel"; break;
default: aa = "default";
}
NanReturnValue(NanSymbol(aa));
NanReturnValue(NanNew(aa));
}
/*
@ -953,7 +969,7 @@ NAN_GETTER(Context2d::GetAntiAlias) {
*/
NAN_SETTER(Context2d::SetAntiAlias) {
String::AsciiValue str(value->ToString());
String::Utf8Value str(value->ToString());
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
cairo_t *ctx = context->context();
cairo_antialias_t a;
@ -986,7 +1002,7 @@ NAN_GETTER(Context2d::GetTextDrawingMode) {
} else {
mode = "unknown";
}
NanReturnValue(NanSymbol(mode));
NanReturnValue(NanNew(mode));
}
/*
@ -994,7 +1010,7 @@ NAN_GETTER(Context2d::GetTextDrawingMode) {
*/
NAN_SETTER(Context2d::SetTextDrawingMode) {
String::AsciiValue str(value->ToString());
String::Utf8Value str(value->ToString());
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
if (0 == strcmp("path", *str)) {
context->state->textDrawingMode = TEXT_DRAW_PATHS;
@ -1018,7 +1034,7 @@ NAN_GETTER(Context2d::GetFilter) {
case CAIRO_FILTER_BILINEAR: filter = "bilinear"; break;
default: filter = "good";
}
NanReturnValue(NanSymbol(filter));
NanReturnValue(NanNew(filter));
}
/*
@ -1026,7 +1042,7 @@ NAN_GETTER(Context2d::GetFilter) {
*/
NAN_SETTER(Context2d::SetFilter) {
String::AsciiValue str(value->ToString());
String::Utf8Value str(value->ToString());
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
cairo_filter_t filter;
if (0 == strcmp("fast", *str)) {
@ -1050,7 +1066,7 @@ NAN_SETTER(Context2d::SetFilter) {
NAN_GETTER(Context2d::GetMiterLimit) {
NanScope();
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
NanReturnValue(Number::New(cairo_get_miter_limit(context->context())));
NanReturnValue(NanNew<Number>(cairo_get_miter_limit(context->context())));
}
/*
@ -1072,7 +1088,7 @@ NAN_SETTER(Context2d::SetMiterLimit) {
NAN_GETTER(Context2d::GetLineWidth) {
NanScope();
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
NanReturnValue(Number::New(cairo_get_line_width(context->context())));
NanReturnValue(NanNew<Number>(cairo_get_line_width(context->context())));
}
/*
@ -1100,7 +1116,7 @@ NAN_GETTER(Context2d::GetLineJoin) {
case CAIRO_LINE_JOIN_ROUND: join = "round"; break;
default: join = "miter";
}
NanReturnValue(NanSymbol(join));
NanReturnValue(NanNew(join));
}
/*
@ -1110,7 +1126,7 @@ NAN_GETTER(Context2d::GetLineJoin) {
NAN_SETTER(Context2d::SetLineJoin) {
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
cairo_t *ctx = context->context();
String::AsciiValue type(value->ToString());
String::Utf8Value type(value->ToString());
if (0 == strcmp("round", *type)) {
cairo_set_line_join(ctx, CAIRO_LINE_JOIN_ROUND);
} else if (0 == strcmp("bevel", *type)) {
@ -1133,7 +1149,7 @@ NAN_GETTER(Context2d::GetLineCap) {
case CAIRO_LINE_CAP_SQUARE: cap = "square"; break;
default: cap = "butt";
}
NanReturnValue(NanSymbol(cap));
NanReturnValue(NanNew(cap));
}
/*
@ -1143,7 +1159,7 @@ NAN_GETTER(Context2d::GetLineCap) {
NAN_SETTER(Context2d::SetLineCap) {
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
cairo_t *ctx = context->context();
String::AsciiValue type(value->ToString());
String::Utf8Value type(value->ToString());
if (0 == strcmp("round", *type)) {
cairo_set_line_cap(ctx, CAIRO_LINE_CAP_ROUND);
} else if (0 == strcmp("square", *type)) {
@ -1164,9 +1180,9 @@ NAN_METHOD(Context2d::IsPointInPath) {
cairo_t *ctx = context->context();
double x = args[0]->NumberValue()
, y = args[1]->NumberValue();
NanReturnValue(Boolean::New(cairo_in_fill(ctx, x, y) || cairo_in_stroke(ctx, x, y)));
NanReturnValue(NanNew<Boolean>(cairo_in_fill(ctx, x, y) || cairo_in_stroke(ctx, x, y)));
}
NanReturnValue(False());
NanReturnValue(NanFalse());
}
/*
@ -1220,7 +1236,7 @@ NAN_METHOD(Context2d::SetStrokePattern) {
NAN_SETTER(Context2d::SetShadowColor) {
short ok;
String::AsciiValue str(value->ToString());
String::Utf8Value str(value->ToString());
uint32_t rgba = rgba_from_string(*str, &ok);
if (ok) {
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
@ -1237,7 +1253,7 @@ NAN_GETTER(Context2d::GetShadowColor) {
char buf[64];
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
rgba_to_string(context->state->shadow, buf, sizeof(buf));
NanReturnValue(String::New(buf));
NanReturnValue(NanNew<String>(buf));
}
/*
@ -1248,7 +1264,7 @@ NAN_METHOD(Context2d::SetFillColor) {
NanScope();
short ok;
if (!args[0]->IsString()) NanReturnUndefined();
String::AsciiValue str(args[0]);
String::Utf8Value str(args[0]);
uint32_t rgba = rgba_from_string(*str, &ok);
if (!ok) NanReturnUndefined();
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
@ -1266,7 +1282,7 @@ NAN_GETTER(Context2d::GetFillColor) {
char buf[64];
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
rgba_to_string(context->state->fill, buf, sizeof(buf));
NanReturnValue(String::New(buf));
NanReturnValue(NanNew<String>(buf));
}
/*
@ -1277,7 +1293,7 @@ NAN_METHOD(Context2d::SetStrokeColor) {
NanScope();
short ok;
if (!args[0]->IsString()) NanReturnUndefined();
String::AsciiValue str(args[0]);
String::Utf8Value str(args[0]);
uint32_t rgba = rgba_from_string(*str, &ok);
if (!ok) NanReturnUndefined();
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
@ -1295,7 +1311,7 @@ NAN_GETTER(Context2d::GetStrokeColor) {
char buf[64];
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
rgba_to_string(context->state->stroke, buf, sizeof(buf));
NanReturnValue(String::New(buf));
NanReturnValue(NanNew<String>(buf));
}
/*
@ -1763,11 +1779,11 @@ NAN_METHOD(Context2d::SetFont) {
|| !args[3]->IsString()
|| !args[4]->IsString()) NanReturnUndefined();
String::AsciiValue weight(args[0]);
String::AsciiValue style(args[1]);
String::Utf8Value weight(args[0]);
String::Utf8Value style(args[1]);
double size = args[2]->NumberValue();
String::AsciiValue unit(args[3]);
String::AsciiValue family(args[4]);
String::Utf8Value unit(args[3]);
String::Utf8Value family(args[4]);
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
@ -1876,7 +1892,7 @@ NAN_METHOD(Context2d::MeasureText) {
cairo_t *ctx = context->context();
String::Utf8Value str(args[0]->ToString());
Local<Object> obj = Object::New();
Local<Object> obj = NanNew<Object>();
#if HAVE_PANGO
@ -1917,21 +1933,21 @@ NAN_METHOD(Context2d::MeasureText) {
y_offset = 0.0;
}
obj->Set(String::New("width"), Number::New(logical_rect.width));
obj->Set(String::New("actualBoundingBoxLeft"),
Number::New(x_offset - PANGO_LBEARING(logical_rect)));
obj->Set(String::New("actualBoundingBoxRight"),
Number::New(x_offset + PANGO_RBEARING(logical_rect)));
obj->Set(String::New("actualBoundingBoxAscent"),
Number::New(-(y_offset+ink_rect.y)));
obj->Set(String::New("actualBoundingBoxDescent"),
Number::New((PANGO_DESCENT(ink_rect) + y_offset)));
obj->Set(String::New("emHeightAscent"),
Number::New(PANGO_ASCENT(logical_rect) - y_offset));
obj->Set(String::New("emHeightDescent"),
Number::New(PANGO_DESCENT(logical_rect) + y_offset));
obj->Set(String::New("alphabeticBaseline"),
Number::New((pango_font_metrics_get_ascent(metrics) / PANGO_SCALE)
obj->Set(NanNew<String>("width"), NanNew<Number>(logical_rect.width));
obj->Set(NanNew<String>("actualBoundingBoxLeft"),
NanNew<Number>(x_offset - PANGO_LBEARING(logical_rect)));
obj->Set(NanNew<String>("actualBoundingBoxRight"),
NanNew<Number>(x_offset + PANGO_RBEARING(logical_rect)));
obj->Set(NanNew<String>("actualBoundingBoxAscent"),
NanNew<Number>(-(y_offset+ink_rect.y)));
obj->Set(NanNew<String>("actualBoundingBoxDescent"),
NanNew<Number>((PANGO_DESCENT(ink_rect) + y_offset)));
obj->Set(NanNew<String>("emHeightAscent"),
NanNew<Number>(PANGO_ASCENT(logical_rect) - y_offset));
obj->Set(NanNew<String>("emHeightDescent"),
NanNew<Number>(PANGO_DESCENT(logical_rect) + y_offset));
obj->Set(NanNew<String>("alphabeticBaseline"),
NanNew<Number>((pango_font_metrics_get_ascent(metrics) / PANGO_SCALE)
+ y_offset));
pango_font_metrics_unref(metrics);
@ -1972,18 +1988,18 @@ NAN_METHOD(Context2d::MeasureText) {
y_offset = 0.0;
}
obj->Set(String::New("width"), Number::New(te.x_advance));
obj->Set(String::New("actualBoundingBoxLeft"),
Number::New(x_offset - te.x_bearing));
obj->Set(String::New("actualBoundingBoxRight"),
Number::New((te.x_bearing + te.width) - x_offset));
obj->Set(String::New("actualBoundingBoxAscent"),
Number::New(-(te.y_bearing + y_offset)));
obj->Set(String::New("actualBoundingBoxDescent"),
Number::New(te.height + te.y_bearing + y_offset));
obj->Set(String::New("emHeightAscent"), Number::New(fe.ascent - y_offset));
obj->Set(String::New("emHeightDescent"), Number::New(fe.descent + y_offset));
obj->Set(String::New("alphabeticBaseline"), Number::New(y_offset));
obj->Set(NanNew<String>("width"), NanNew<Number>(te.x_advance));
obj->Set(NanNew<String>("actualBoundingBoxLeft"),
NanNew<Number>(x_offset - te.x_bearing));
obj->Set(NanNew<String>("actualBoundingBoxRight"),
NanNew<Number>((te.x_bearing + te.width) - x_offset));
obj->Set(NanNew<String>("actualBoundingBoxAscent"),
NanNew<Number>(-(te.y_bearing + y_offset)));
obj->Set(NanNew<String>("actualBoundingBoxDescent"),
NanNew<Number>(te.height + te.y_bearing + y_offset));
obj->Set(NanNew<String>("emHeightAscent"), NanNew<Number>(fe.ascent - y_offset));
obj->Set(NanNew<String>("emHeightDescent"), NanNew<Number>(fe.descent + y_offset));
obj->Set(NanNew<String>("alphabeticBaseline"), NanNew<Number>(y_offset));
#endif
@ -2018,6 +2034,87 @@ NAN_METHOD(Context2d::SetTextAlignment) {
NanReturnUndefined();
}
/*
* Set line dash
* ref: http://www.w3.org/TR/2dcontext/#dom-context-2d-setlinedash
*/
NAN_METHOD(Context2d::SetLineDash) {
NanScope();
if (!args[0]->IsArray()) NanReturnUndefined();
Handle<Array> dash = Handle<Array>::Cast(args[0]);
uint32_t dashes = dash->Length() & 1 ? dash->Length() * 2 : dash->Length();
std::vector<double> a(dashes);
for (uint32_t i=0; i<dashes; i++) {
Local<Value> d = dash->Get(i % dash->Length());
if (!d->IsNumber()) NanReturnUndefined();
a[i] = d->NumberValue();
if (a[i] < 0 || isnan(a[i]) || isinf(a[i])) NanReturnUndefined();
}
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
cairo_t *ctx = context->context();
double offset;
cairo_get_dash(ctx, NULL, &offset);
cairo_set_dash(ctx, a.data(), dashes, offset);
NanReturnUndefined();
}
/*
* Get line dash
* ref: http://www.w3.org/TR/2dcontext/#dom-context-2d-setlinedash
*/
NAN_METHOD(Context2d::GetLineDash) {
NanScope();
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
cairo_t *ctx = context->context();
int dashes = cairo_get_dash_count(ctx);
std::vector<double> a(dashes);
cairo_get_dash(ctx, a.data(), NULL);
Local<Array> dash = NanNew<Array>(dashes);
for (int i=0; i<dashes; i++)
dash->Set(NanNew<Number>(i), NanNew<Number>(a[i]));
NanReturnValue(dash);
}
/*
* Set line dash offset
* ref: http://www.w3.org/TR/2dcontext/#dom-context-2d-setlinedash
*/
NAN_SETTER(Context2d::SetLineDashOffset) {
NanScope();
double offset = value->NumberValue();
if (isnan(offset) || isinf(offset)) return;
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
cairo_t *ctx = context->context();
int dashes = cairo_get_dash_count(ctx);
std::vector<double> a(dashes);
cairo_get_dash(ctx, a.data(), NULL);
cairo_set_dash(ctx, a.data(), dashes, offset);
}
/*
* Get line dash offset
* ref: http://www.w3.org/TR/2dcontext/#dom-context-2d-setlinedash
*/
NAN_GETTER(Context2d::GetLineDashOffset) {
NanScope();
Context2d *context = ObjectWrap::Unwrap<Context2d>(args.This());
cairo_t *ctx = context->context();
double offset;
cairo_get_dash(ctx, NULL, &offset);
NanReturnValue(NanNew<Number>(offset));
}
/*
* Fill the rectangle defined by x, y, width and height.
*/

4
src/CanvasRenderingContext2d.h

@ -100,6 +100,8 @@ class Context2d: public node::ObjectWrap {
static NAN_METHOD(SetStrokePattern);
static NAN_METHOD(SetTextBaseline);
static NAN_METHOD(SetTextAlignment);
static NAN_METHOD(SetLineDash);
static NAN_METHOD(GetLineDash);
static NAN_METHOD(MeasureText);
static NAN_METHOD(BezierCurveTo);
static NAN_METHOD(QuadraticCurveTo);
@ -121,6 +123,7 @@ class Context2d: public node::ObjectWrap {
static NAN_GETTER(GetLineCap);
static NAN_GETTER(GetLineJoin);
static NAN_GETTER(GetLineWidth);
static NAN_GETTER(GetLineDashOffset);
static NAN_GETTER(GetShadowOffsetX);
static NAN_GETTER(GetShadowOffsetY);
static NAN_GETTER(GetShadowBlur);
@ -135,6 +138,7 @@ class Context2d: public node::ObjectWrap {
static NAN_SETTER(SetLineCap);
static NAN_SETTER(SetLineJoin);
static NAN_SETTER(SetLineWidth);
static NAN_SETTER(SetLineDashOffset);
static NAN_SETTER(SetShadowOffsetX);
static NAN_SETTER(SetShadowOffsetY);
static NAN_SETTER(SetShadowBlur);

12
src/FontFace.cc

@ -6,8 +6,6 @@
#include "FontFace.h"
#include "nan.h"
Persistent<FunctionTemplate> FontFace::constructor;
/*
@ -30,13 +28,13 @@ FontFace::Initialize(Handle<Object> target) {
NanScope();
// Constructor
Local<FunctionTemplate> ctor = FunctionTemplate::New(FontFace::New);
NanAssignPersistent(FunctionTemplate, constructor, ctor);
Local<FunctionTemplate> ctor = NanNew<FunctionTemplate>(FontFace::New);
NanAssignPersistent(constructor, ctor);
ctor->InstanceTemplate()->SetInternalFieldCount(1);
ctor->SetClassName(NanSymbol("FontFace"));
ctor->SetClassName(NanNew("FontFace"));
// Prototype
target->Set(NanSymbol("FontFace"), ctor->GetFunction());
target->Set(NanNew("FontFace"), ctor->GetFunction());
}
/*
@ -60,7 +58,7 @@ NAN_METHOD(FontFace::New) {
return NanThrowError("Wrong argument types passed to FontFace constructor");
}
String::AsciiValue filePath(args[0]);
String::Utf8Value filePath(args[0]);
int faceIdx = int(args[1]->NumberValue());
FT_Face ftFace;

54
src/Image.cc

@ -38,28 +38,28 @@ void
Image::Initialize(Handle<Object> target) {
NanScope();
Local<FunctionTemplate> ctor = FunctionTemplate::New(Image::New);
NanAssignPersistent(FunctionTemplate, constructor, ctor);
Local<FunctionTemplate> ctor = NanNew<FunctionTemplate>(Image::New);
NanAssignPersistent(constructor, ctor);
ctor->InstanceTemplate()->SetInternalFieldCount(1);
ctor->SetClassName(NanSymbol("Image"));
ctor->SetClassName(NanNew("Image"));
ctor->InstanceTemplate()->SetInternalFieldCount(1);
ctor->SetClassName(NanSymbol("Image"));
ctor->SetClassName(NanNew("Image"));
// Prototype
Local<ObjectTemplate> proto = ctor->PrototypeTemplate();
proto->SetAccessor(NanSymbol("source"), GetSource, SetSource);
proto->SetAccessor(NanSymbol("complete"), GetComplete);
proto->SetAccessor(NanSymbol("width"), GetWidth);
proto->SetAccessor(NanSymbol("height"), GetHeight);
proto->SetAccessor(NanSymbol("onload"), GetOnload, SetOnload);
proto->SetAccessor(NanSymbol("onerror"), GetOnerror, SetOnerror);
proto->SetAccessor(NanNew("source"), GetSource, SetSource);
proto->SetAccessor(NanNew("complete"), GetComplete);
proto->SetAccessor(NanNew("width"), GetWidth);
proto->SetAccessor(NanNew("height"), GetHeight);
proto->SetAccessor(NanNew("onload"), GetOnload, SetOnload);
proto->SetAccessor(NanNew("onerror"), GetOnerror, SetOnerror);
#if CAIRO_VERSION_MINOR >= 10
proto->SetAccessor(NanSymbol("dataMode"), GetDataMode, SetDataMode);
ctor->Set(NanSymbol("MODE_IMAGE"), Number::New(DATA_IMAGE));
ctor->Set(NanSymbol("MODE_MIME"), Number::New(DATA_MIME));
proto->SetAccessor(NanNew("dataMode"), GetDataMode, SetDataMode);
ctor->Set(NanNew("MODE_IMAGE"), NanNew<Number>(DATA_IMAGE));
ctor->Set(NanNew("MODE_MIME"), NanNew<Number>(DATA_MIME));
#endif
target->Set(NanSymbol("Image"), ctor->GetFunction());
target->Set(NanNew("Image"), ctor->GetFunction());
}
/*
@ -81,7 +81,7 @@ NAN_METHOD(Image::New) {
NAN_GETTER(Image::GetComplete) {
NanScope();
Image *img = ObjectWrap::Unwrap<Image>(args.This());
NanReturnValue(Boolean::New(Image::COMPLETE == img->state));
NanReturnValue(NanNew<Boolean>(Image::COMPLETE == img->state));
}
#if CAIRO_VERSION_MINOR >= 10
@ -93,7 +93,7 @@ NAN_GETTER(Image::GetComplete) {
NAN_GETTER(Image::GetDataMode) {
NanScope();
Image *img = ObjectWrap::Unwrap<Image>(args.This());
NanReturnValue(Number::New(img->data_mode));
NanReturnValue(NanNew<Number>(img->data_mode));
}
/*
@ -117,7 +117,7 @@ NAN_SETTER(Image::SetDataMode) {
NAN_GETTER(Image::GetWidth) {
NanScope();
Image *img = ObjectWrap::Unwrap<Image>(args.This());
NanReturnValue(Number::New(img->width));
NanReturnValue(NanNew<Number>(img->width));
}
/*
* Get height.
@ -126,7 +126,7 @@ NAN_GETTER(Image::GetWidth) {
NAN_GETTER(Image::GetHeight) {
NanScope();
Image *img = ObjectWrap::Unwrap<Image>(args.This());
NanReturnValue(Number::New(img->height));
NanReturnValue(NanNew<Number>(img->height));
}
/*
@ -136,7 +136,7 @@ NAN_GETTER(Image::GetHeight) {
NAN_GETTER(Image::GetSource) {
NanScope();
Image *img = ObjectWrap::Unwrap<Image>(args.This());
NanReturnValue(String::New(img->filename ? img->filename : ""));
NanReturnValue(NanNew<String>(img->filename ? img->filename : ""));
}
/*
@ -147,7 +147,7 @@ void
Image::clearData() {
if (_surface) {
cairo_surface_destroy(_surface);
V8::AdjustAmountOfExternalAllocatedMemory(-_data_len);
NanAdjustExternalMemory(-_data_len);
_data_len = 0;
_surface = NULL;
}
@ -175,7 +175,7 @@ NAN_SETTER(Image::SetSource) {
// url string
if (value->IsString()) {
String::AsciiValue src(value);
String::Utf8Value src(value);
if (img->filename) free(img->filename);
img->filename = strdup(*src);
status = img->load();
@ -359,7 +359,7 @@ Image::loaded() {
width = cairo_image_surface_get_width(_surface);
height = cairo_image_surface_get_height(_surface);
_data_len = height * cairo_image_surface_get_stride(_surface);
V8::AdjustAmountOfExternalAllocatedMemory(_data_len);
NanAdjustExternalMemory(_data_len);
if (onload != NULL) {
onload->Call(0, NULL);
@ -517,7 +517,7 @@ Image::loadGIFFromBuffer(uint8_t *buf, unsigned len) {
#endif
if (GIF_OK != DGifSlurp(gif)) {
DGifCloseFile(gif);
GIF_CLOSE_FILE(gif);
return CAIRO_STATUS_READ_ERROR;
}
@ -526,7 +526,7 @@ Image::loadGIFFromBuffer(uint8_t *buf, unsigned len) {
uint8_t *data = (uint8_t *) malloc(width * height * 4);
if (!data) {
DGifCloseFile(gif);
GIF_CLOSE_FILE(gif);
return CAIRO_STATUS_NO_MEMORY;
}
@ -608,7 +608,7 @@ Image::loadGIFFromBuffer(uint8_t *buf, unsigned len) {
}
}
DGifCloseFile(gif);
GIF_CLOSE_FILE(gif);
// New image surface
_surface = cairo_image_surface_create_for_data(
@ -807,7 +807,7 @@ Image::decodeJPEGBufferIntoMimeSurface(uint8_t *buf, unsigned len) {
void
clearMimeData(void *closure) {
V8::AdjustAmountOfExternalAllocatedMemory(-((read_closure_t *)closure)->len);
NanAdjustExternalMemory(-((read_closure_t *)closure)->len);
free(((read_closure_t *) closure)->buf);
free(closure);
}
@ -834,7 +834,7 @@ Image::assignDataAsMime(uint8_t *data, int len, const char *mime_type) {
mime_closure->buf = mime_data;
mime_closure->len = len;
V8::AdjustAmountOfExternalAllocatedMemory(len);
NanAdjustExternalMemory(len);
return cairo_surface_set_mime_data(_surface
, mime_type

9
src/Image.h

@ -8,7 +8,6 @@
#ifndef __NODE_IMAGE_H__
#define __NODE_IMAGE_H__
#include "nan.h"
#include "Canvas.h"
#ifdef HAVE_JPEG
@ -18,8 +17,16 @@
#ifdef HAVE_GIF
#include <gif_lib.h>
#if GIFLIB_MAJOR > 5 || GIFLIB_MAJOR == 5 && GIFLIB_MINOR >= 1
#define GIF_CLOSE_FILE(gif) DGifCloseFile(gif, NULL)
#else
#define GIF_CLOSE_FILE(gif) DGifCloseFile(gif)
#endif
#endif
class Image: public node::ObjectWrap {
public:
char *filename;

18
src/ImageData.cc

@ -18,16 +18,16 @@ ImageData::Initialize(Handle<Object> target) {
NanScope();
// Constructor
Local<FunctionTemplate> ctor = FunctionTemplate::New(ImageData::New);
NanAssignPersistent(FunctionTemplate, constructor, ctor);
Local<FunctionTemplate> ctor = NanNew<FunctionTemplate>(ImageData::New);
NanAssignPersistent(constructor, ctor);
ctor->InstanceTemplate()->SetInternalFieldCount(1);
ctor->SetClassName(NanSymbol("ImageData"));
ctor->SetClassName(NanNew("ImageData"));
// Prototype
Local<ObjectTemplate> proto = ctor->PrototypeTemplate();
proto->SetAccessor(NanSymbol("width"), GetWidth);
proto->SetAccessor(NanSymbol("height"), GetHeight);
target->Set(NanSymbol("ImageData"), ctor->GetFunction());
proto->SetAccessor(NanNew("width"), GetWidth);
proto->SetAccessor(NanNew("height"), GetHeight);
target->Set(NanNew("ImageData"), ctor->GetFunction());
}
/*
@ -43,7 +43,7 @@ NAN_METHOD(ImageData::New) {
PixelArray *arr = ObjectWrap::Unwrap<PixelArray>(obj);
ImageData *imageData = new ImageData(arr);
args.This()->Set(NanSymbol("data"), args[0]);
args.This()->Set(NanNew("data"), args[0]);
imageData->Wrap(args.This());
NanReturnValue(args.This());
}
@ -55,7 +55,7 @@ NAN_METHOD(ImageData::New) {
NAN_GETTER(ImageData::GetWidth) {
NanScope();
ImageData *imageData = ObjectWrap::Unwrap<ImageData>(args.This());
NanReturnValue(Number::New(imageData->pixelArray()->width()));
NanReturnValue(NanNew<Number>(imageData->pixelArray()->width()));
}
/*
@ -65,5 +65,5 @@ NAN_GETTER(ImageData::GetWidth) {
NAN_GETTER(ImageData::GetHeight) {
NanScope();
ImageData *imageData = ObjectWrap::Unwrap<ImageData>(args.This());
NanReturnValue(Number::New(imageData->pixelArray()->height()));
NanReturnValue(NanNew<Number>(imageData->pixelArray()->height()));
}

25
src/JPEGStream.h

@ -6,7 +6,6 @@
#ifndef __NODE_JPEG_STREAM_H__
#define __NODE_JPEG_STREAM_H__
#include "nan.h"
#include "Canvas.h"
#include <jpeglib.h>
#include <jerror.h>
@ -34,11 +33,11 @@ empty_closure_output_buffer(j_compress_ptr cinfo){
closure_destination_mgr *dest = (closure_destination_mgr *) cinfo->dest;
Local<Object> buf = NanNewBufferHandle((char *)dest->buffer, dest->bufsize);
Local<Value> argv[3] = {
NanNewLocal<Value>(Null())
, NanNewLocal<Value>(buf)
, Integer::New(dest->bufsize)
NanNew(NanNull())
, NanNew(buf)
, NanNew<Integer>(dest->bufsize)
};
dest->closure->fn->Call(Context::GetCurrent()->Global(), 3, argv);
NanMakeCallback(NanGetCurrentContext()->Global(), dest->closure->fn, 3, argv);
cinfo->dest->next_output_byte = dest->buffer;
cinfo->dest->free_in_buffer = dest->bufsize;
return true;
@ -53,21 +52,21 @@ term_closure_destination(j_compress_ptr cinfo){
Local<Object> buf = NanNewBufferHandle((char *)dest->buffer, remaining);
Local<Value> data_argv[3] = {
NanNewLocal<Value>(Null())
, NanNewLocal<Value>(buf)
, Integer::New(remaining)
NanNew(NanNull())
, NanNew(buf)
, NanNew<Integer>(remaining)
};
dest->closure->fn->Call(Context::GetCurrent()->Global(), 3, data_argv);
NanMakeCallback(NanGetCurrentContext()->Global(), dest->closure->fn, 3, data_argv);
// emit "end"
Local<Value> end_argv[3] = {
NanNewLocal<Value>(Null())
, NanNewLocal<Value>(Null())
, Integer::New(0)
NanNew(NanNull())
, NanNew(NanNull())
, NanNew<Integer>(0)
};
dest->closure->fn->Call(Context::GetCurrent()->Global(), 3, end_argv);
NanMakeCallback(NanGetCurrentContext()->Global(), dest->closure->fn, 3, end_argv);
}
void

18
src/PixelArray.cc

@ -9,8 +9,6 @@
#include <stdlib.h>
#include <string.h>
#include "nan.h"
Persistent<FunctionTemplate> PixelArray::constructor;
/*
@ -22,15 +20,15 @@ PixelArray::Initialize(Handle<Object> target) {
NanScope();
// Constructor
Local<FunctionTemplate> ctor = FunctionTemplate::New(PixelArray::New);
NanAssignPersistent(FunctionTemplate, constructor, ctor);
Local<FunctionTemplate> ctor = NanNew<FunctionTemplate>(PixelArray::New);
NanAssignPersistent(constructor, ctor);
ctor->InstanceTemplate()->SetInternalFieldCount(1);
ctor->SetClassName(NanSymbol("CanvasPixelArray"));
ctor->SetClassName(NanNew("CanvasPixelArray"));
// Prototype
Local<ObjectTemplate> proto = ctor->InstanceTemplate();
proto->SetAccessor(NanSymbol("length"), GetLength);
target->Set(NanSymbol("CanvasPixelArray"), ctor->GetFunction());
proto->SetAccessor(NanNew("length"), GetLength);
target->Set(NanNew("CanvasPixelArray"), ctor->GetFunction());
}
/*
@ -82,7 +80,7 @@ NAN_METHOD(PixelArray::New) {
NAN_GETTER(PixelArray::GetLength) {
NanScope();
NanReturnValue(Number::New(args.This()->GetIndexedPropertiesPixelDataLength()));
NanReturnValue(NanNew<Number>(args.This()->GetIndexedPropertiesPixelDataLength()));
}
/*
@ -142,7 +140,7 @@ uint8_t *
PixelArray::alloc() {
int len = length();
_data = (uint8_t *) calloc(1, len);
V8::AdjustAmountOfExternalAllocatedMemory(len);
NanAdjustExternalMemory(len);
return _data;
}
@ -151,6 +149,6 @@ PixelArray::alloc() {
*/
PixelArray::~PixelArray() {
V8::AdjustAmountOfExternalAllocatedMemory(-length());
NanAdjustExternalMemory(-length());
free(_data);
}

6
src/closure.h

@ -16,7 +16,7 @@
#define PAGE_SIZE 4096
#endif
#include "nan.h"
#include <nan.h>
/*
* PNG stream closure.
@ -50,7 +50,7 @@ closure_init(closure_t *closure, Canvas *canvas, unsigned int compression_level,
}
/*
* Free the given closure's data,
* Free the given closure's data,
* and hint V8 at the memory dealloc.
*/
@ -58,7 +58,7 @@ void
closure_destroy(closure_t *closure) {
if (closure->len) {
free(closure->data);
V8::AdjustAmountOfExternalAllocatedMemory(- (intptr_t) closure->max_len);
NanAdjustExternalMemory(-((intptr_t) closure->max_len));
}
}

4
src/color.cc

@ -350,10 +350,10 @@ rgba_from_rgba_string(const char *str, short *ok) {
if ('0' == *str) ++str;
if ('.' == *str) {
++str;
float n = .1;
float n = .1f;
while (*str >= '0' && *str <= '9') {
a += (*str++ - '0') * n;
n *= .1;
n *= .1f;
}
}
}

8
src/init.cc

@ -32,7 +32,7 @@ init (Handle<Object> target) {
FontFace::Initialize(target);
#endif
target->Set(String::New("cairoVersion"), String::New(cairo_version_string()));
target->Set(NanNew<String>("cairoVersion"), NanNew<String>(cairo_version_string()));
#ifdef HAVE_JPEG
#ifndef JPEG_LIB_VERSION_MAJOR
@ -57,16 +57,16 @@ init (Handle<Object> target) {
} else {
snprintf(jpeg_version, 10, "%d", JPEG_LIB_VERSION_MAJOR);
}
target->Set(String::New("jpegVersion"), String::New(jpeg_version));
target->Set(NanNew<String>("jpegVersion"), NanNew<String>(jpeg_version));
#endif
#ifdef HAVE_GIF
#ifndef GIF_LIB_VERSION
char gif_version[10];
snprintf(gif_version, 10, "%d.%d.%d", GIFLIB_MAJOR, GIFLIB_MINOR, GIFLIB_RELEASE);
target->Set(String::New("gifVersion"), String::New(gif_version));
target->Set(NanNew<String>("gifVersion"), NanNew<String>(gif_version));
#else
target->Set(String::New("gifVersion"), String::New(GIF_LIB_VERSION));
target->Set(NanNew<String>("gifVersion"), NanNew<String>(GIF_LIB_VERSION));
#endif
#endif
}

6
test/canvas.test.js

@ -76,6 +76,10 @@ module.exports = {
}
},
'test .PixelArray': function(){
assert.equal(typeof Canvas.PixelArray, 'function');
},
'test color serialization': function(){
var canvas = new Canvas(200, 200)
, ctx = canvas.getContext('2d');
@ -161,6 +165,8 @@ module.exports = {
assert('image' == canvas.type);
var canvas = new Canvas(10, 10, 'pdf');
assert('pdf' == canvas.type);
var canvas = new Canvas(10, 10, 'svg');
assert('svg' == canvas.type);
var canvas = new Canvas(10, 10, 'hey');
assert('image' == canvas.type);
},

67
test/public/tests.js

@ -1850,4 +1850,69 @@ tests['putImageData() png data 3'] = function(ctx, done){
};
img.onerror = function(){}
img.src = 'state.png';
};
};
tests['setLineDash'] = function(ctx, done){
ctx.setLineDash([10, 5, 25, 15]);
ctx.lineWidth = 17;
var y=5;
var line = function(lineDash, color){
ctx.setLineDash(lineDash);
if (color) ctx.strokeStyle = color;
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(200, y);
ctx.stroke();
y += ctx.lineWidth + 4;
};
line([15, 30], "blue");
line([], "black");
line([5,10,15,20,25,30,35,40,45,50], "purple");
line([8], "green");
line([3, 3, -30], "red");
line([4, Infinity, 4]);
line([10, 10, NaN]);
line((function(){
ctx.setLineDash([8]);
var a = ctx.getLineDash();
a[0] -= 3;
a.push(20);
return a;
})(), "orange");
};
tests['lineDashOffset'] = function(ctx, done){
ctx.setLineDash([10, 5, 25, 15]);
ctx.lineWidth = 4;
var y=5;
var line = function(lineDashOffset, color){
ctx.lineDashOffset = lineDashOffset;
if (color) ctx.strokeStyle = color;
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(200, y);
ctx.stroke();
y += ctx.lineWidth + 4;
};
line(-10, "black");
line(0);
line(10);
line(20);
line(30);
line(40, "blue");
line(NaN)
line(50, "green");
line(Infinity)
line(60, "orange");
line(-Infinity)
line(70, "purple");
line(void 0)
line(80, "black");
line(ctx.lineDashOffset + 10);
for (var i=0; i<10; i++)
line(90 + i/5, "red");
}

Loading…
Cancel
Save