Skip to content

Commit

Permalink
feat: Context.prototype.getImageData (experimental), for gliffy#3, for
Browse files Browse the repository at this point in the history
  • Loading branch information
zenozeng committed Feb 4, 2022
1 parent 5afaf23 commit 3991b8e
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 41 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,16 @@ ctx.fillRect(100,100,100,100);
const mySerializedSVG = ctx.getSerializedSvg();
```

## Tests

https://zenozeng.github.io/p5.js-svg/test/

## CHANGELOG

## v2.2.0

- feat: Context.prototype.getImageData (experimental) for https://github.com/gliffy/canvas2svg/issues/3 and https://github.com/zenozeng/p5.js-svg/issues/203

## v2.1.0

- feat: SVGCanvasElement(options)
Expand Down
15 changes: 14 additions & 1 deletion context.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
*/

import * as utils from './utils';
import imageUtils from './image';

export default (function () {
"use strict";
Expand Down Expand Up @@ -1306,12 +1307,24 @@ export default (function () {
return new DOMPoint(x, y).matrixTransform(this.__transformMatrix)
}

/**
*
* @param {*} sx The x-axis coordinate of the top-left corner of the rectangle from which the ImageData will be extracted.
* @param {*} sy The y-axis coordinate of the top-left corner of the rectangle from which the ImageData will be extracted.
* @param {*} sw The width of the rectangle from which the ImageData will be extracted. Positive values are to the right, and negative to the left.
* @param {*} sh The height of the rectangle from which the ImageData will be extracted. Positive values are down, and negative are up.
* @param {Boolean} options.async Will return a Promise<ImageData> if true, must be set to true
* @returns An ImageData object containing the image data for the rectangle of the canvas specified. The coordinates of the rectangle's top-left corner are (sx, sy), while the coordinates of the bottom corner are (sx + sw, sy + sh).
*/
Context.prototype.getImageData = function(sx, sy, sw, sh, options) {
return imageUtils.getImageData(this.getSvg(), this.width, this.height, sx, sy, sw, sh, options);
};

/**
* Not yet implemented
*/
Context.prototype.drawFocusRing = function () {};
Context.prototype.createImageData = function () {};
Context.prototype.getImageData = function () {};
Context.prototype.putImageData = function () {};
Context.prototype.globalCompositeOperation = function () {};

Expand Down
49 changes: 10 additions & 39 deletions element.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Context from './context';
import imageUtils from './image';

function SVGCanvasElement(options) {

Expand Down Expand Up @@ -74,45 +75,15 @@ SVGCanvasElement.prototype.toObjectURL = function() {
return URL.createObjectURL(svg);
};

SVGCanvasElement.prototype.toDataURL = function(type, options) {
var xml = new XMLSerializer().serializeToString(this.svg);

// documentMode is an IE-only property
// http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
// http://stackoverflow.com/questions/10964966/detect-ie-version-prior-to-v9-in-javascript
var isIE = document.documentMode;

if (isIE) {
// This is patch from canvas2svg
// IE search for a duplicate xmnls because they didn't implement setAttributeNS correctly
var xmlns = /xmlns="http:\/\/www\.w3\.org\/2000\/svg".+xmlns="http:\/\/www\.w3\.org\/2000\/svg/gi;
if(xmlns.test(xml)) {
xml = xml.replace('xmlns="http://www.w3.org/2000/svg','xmlns:xlink="http://www.w3.org/1999/xlink');
}
}

var SVGDataURL = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(xml);
if (type === "image/svg+xml" || !type) {
return SVGDataURL;
}
if (type === "image/jpeg" || type === "image/png") {
var canvas = document.createElement('canvas');
canvas.width = this.width;
canvas.height = this.height;
var ctx = canvas.getContext('2d');
var img = new Image();
img.src = SVGDataURL;
if (img.complete && img.width > 0 && img.height > 0) {
// for chrome, it's ready immediately
ctx.drawImage(img, 0, 0);
return canvas.toDataURL(type, options);
} else {
// for firefox, it's not possible to provide sync api in current thread
// and web worker doesn't provide canvas API, so
throw new Error('svgcanvas.toDataURL() for jpeg/png is only available in Chrome.');
}
}
throw new Error('Unknown type for SVGCanvas.prototype.toDataURL, please use image/jpeg | image/png | image/svg+xml.');
/**
* toDataURL returns a data URI containing a representation of the image in the format specified by the type parameter.
*
* @param {String} type A DOMString indicating the image format. The default type is image/svg+xml; this image format will be also used if the specified type is not supported.
* @param {Number} encoderOptions A Number between 0 and 1 indicating the image quality to be used when creating images using file formats that support lossy compression (such as image/jpeg or image/webp). A user agent will use its default quality value if this option is not specified, or if the number is outside the allowed range.
* @param {Boolean} options.async Will return a Promise<String> if true, must be set to true if type is not image/svg+xml
*/
SVGCanvasElement.prototype.toDataURL = function(type, encoderOptions, options) {
return imageUtils.toDataURL(this.svg, this.width, this.height, type, encoderOptions, options)
};

SVGCanvasElement.prototype.addEventListener = function() {
Expand Down
86 changes: 86 additions & 0 deletions image.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
class ImageUtils {

/**
* Convert svg dataurl to canvas element
*
* @private
*/
async svg2canvas(svgDataURL, width, height) {
const svgImage = await new Promise((resolve) => {
var svgImage = new Image();
svgImage.onload = function() {
resolve(svgImage);
}
svgImage.src = svgDataURL;
})
var canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
ctx.drawImage(svgImage, 0, 0);
return canvas;
}

toDataURL(svgNode, width, height, type, encoderOptions, options) {
var xml = new XMLSerializer().serializeToString(svgNode);

// documentMode is an IE-only property
// http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
// http://stackoverflow.com/questions/10964966/detect-ie-version-prior-to-v9-in-javascript
var isIE = document.documentMode;

if (isIE) {
// This is patch from canvas2svg
// IE search for a duplicate xmnls because they didn't implement setAttributeNS correctly
var xmlns = /xmlns="http:\/\/www\.w3\.org\/2000\/svg".+xmlns="http:\/\/www\.w3\.org\/2000\/svg/gi;
if(xmlns.test(xml)) {
xml = xml.replace('xmlns="http://www.w3.org/2000/svg','xmlns:xlink="http://www.w3.org/1999/xlink');
}
}

if (!options) {
options = {}
}

var SVGDataURL = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(xml);
if (type === "image/svg+xml" || !type) {
if (options.async) {
return Promise.resolve(SVGDataURL)
}
return SVGDataURL;
}
if (type === "image/jpeg" || type === "image/png") {
if (!options.async) {
throw new Error('svgcanvas: options.async must be set to true if type is image/jpeg | image/png')
}
return (async () => {
const canvas = await this.svg2canvas(SVGDataURL, width, height);
const dataUrl = canvas.toDataURL(type, encoderOptions);
canvas.remove();
return dataUrl;
})()
}
throw new Error('svgcanvas: Unknown type for toDataURL, please use image/jpeg | image/png | image/svg+xml.');
}

getImageData(svgNode, width, height, sx, sy, sw, sh, options) {
if (!options) {
options = {}
}
if (!options.async) {
throw new Error('svgcanvas: options.async must be set to true for getImageData')
}
const svgDataURL = this.toDataURL(svgNode, width, height, 'image/svg+xml');
return (async () => {
const canvas = await this.svg2canvas(svgDataURL, width, height);
const ctx = canvas.getContext('2d')
const imageData = ctx.getImageData(sx, sy, sw, sh);
canvas.remove();
return imageData;
})()
}
}

const utils = new ImageUtils();

export default utils;
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "svgcanvas",
"version": "2.1.0",
"version": "2.2.0",
"description": "svgcanvas",
"main": "dist/svgcanvas.js",
"scripts": {
Expand Down

0 comments on commit 3991b8e

Please sign in to comment.