diff --git a/src/containers/paper-canvas.jsx b/src/containers/paper-canvas.jsx index 6f1ae137..0f5075e6 100644 --- a/src/containers/paper-canvas.jsx +++ b/src/containers/paper-canvas.jsx @@ -7,13 +7,11 @@ import Formats from '../lib/format'; import Modes from '../lib/modes'; import log from '../log/log'; -import {inlineSvgFonts} from 'scratch-svg-renderer'; - -import {trim} from '../helper/bitmap'; +import {convertToBitmap, convertToVector} from '../helper/bitmap'; import {performSnapshot} from '../helper/undo'; import {undoSnapshot, clearUndoState} from '../reducers/undo'; import {isGroup, ungroupItems} from '../helper/group'; -import {clearRaster, getRaster, setupLayers, hideGuideLayers, showGuideLayers} from '../helper/layer'; +import {clearRaster, getRaster, setupLayers} from '../helper/layer'; import {deleteSelection, getSelectedLeafItems} from '../helper/selection'; import {clearSelectedItems, setSelectedItems} from '../reducers/selected-items'; import {ART_BOARD_WIDTH, ART_BOARD_HEIGHT, pan, resetZoom, zoomOnFixedPoint} from '../helper/view'; @@ -31,8 +29,6 @@ class PaperCanvas extends React.Component { constructor (props) { super(props); bindAll(this, [ - 'convertToBitmap', - 'convertToVector', 'setCanvas', 'importSvg', 'handleKeyDown', @@ -61,9 +57,9 @@ class PaperCanvas extends React.Component { this.switchCostume( newProps.imageFormat, newProps.image, newProps.rotationCenterX, newProps.rotationCenterY); } else if (isVector(this.props.format) && newProps.format === Formats.BITMAP) { - this.convertToBitmap(); + convertToBitmap(this.props.clearSelectedItems, this.props.onUpdateImage); } else if (isBitmap(this.props.format) && newProps.format === Formats.VECTOR) { - this.convertToVector(); + convertToVector(this.props.clearSelectedItems, this.props.onUpdateImage); } } componentWillUnmount () { @@ -82,60 +78,6 @@ class PaperCanvas extends React.Component { } } } - convertToBitmap () { - // @todo if the active layer contains only rasters, drawing them directly to the raster layer - // would be more efficient. - - // Export svg - const guideLayers = hideGuideLayers(true /* includeRaster */); - const bounds = paper.project.activeLayer.bounds; - const svg = paper.project.exportSVG({ - bounds: 'content', - matrix: new paper.Matrix().translate(-bounds.x, -bounds.y) - }); - showGuideLayers(guideLayers); - - // Get rid of anti-aliasing - // @todo get crisp text? - svg.setAttribute('shape-rendering', 'crispEdges'); - inlineSvgFonts(svg); - const svgString = (new XMLSerializer()).serializeToString(svg); - - // Put anti-aliased SVG into image, and dump image back into canvas - const img = new Image(); - img.onload = () => { - if (img.width && img.height) { - getRaster().drawImage( - img, - new paper.Point(Math.floor(bounds.topLeft.x), Math.floor(bounds.topLeft.y))); - } - paper.project.activeLayer.removeChildren(); - this.props.onUpdateImage(); - }; - img.onerror = () => { - // Fallback if browser does not support SVG data URIs in images. - // The problem with rasterize is that it will anti-alias. - const raster = paper.project.activeLayer.rasterize(72, false /* insert */); - raster.onLoad = () => { - if (raster.canvas.width && raster.canvas.height) { - getRaster().drawImage(raster.canvas, raster.bounds.topLeft); - } - paper.project.activeLayer.removeChildren(); - this.props.onUpdateImage(); - }; - }; - // Hash tags will break image loading without being encoded first - img.src = `data:image/svg+xml;utf8,${encodeURIComponent(svgString)}`; - } - convertToVector () { - this.props.clearSelectedItems(); - const trimmedRaster = trim(getRaster()); - if (trimmedRaster) { - paper.project.activeLayer.addChild(trimmedRaster); - } - clearRaster(); - this.props.onUpdateImage(); - } switchCostume (format, image, rotationCenterX, rotationCenterY) { for (const layer of paper.project.layers) { if (layer.data.isRasterLayer) { diff --git a/src/helper/bitmap.js b/src/helper/bitmap.js index 01a292ba..1aac8241 100644 --- a/src/helper/bitmap.js +++ b/src/helper/bitmap.js @@ -1,4 +1,6 @@ import paper from '@scratch/paper'; +import {clearRaster, getRaster, hideGuideLayers, showGuideLayers} from '../helper/layer'; +import {inlineSvgFonts} from 'scratch-svg-renderer'; const forEachLinePoint = function (point1, point2, callback) { // Bresenham line algorithm @@ -141,7 +143,7 @@ const getHitBounds = function (raster) { return new paper.Rectangle(left, top, right - left, bottom - top); }; -const trim = function (raster) { +const trim_ = function (raster) { const hitBounds = getHitBounds(raster); if (hitBounds.width && hitBounds.height) { return raster.getSubRaster(getHitBounds(raster)); @@ -149,10 +151,69 @@ const trim = function (raster) { return null; }; +const convertToBitmap = function (clearSelectedItems, onUpdateImage) { + // @todo if the active layer contains only rasters, drawing them directly to the raster layer + // would be more efficient. + + clearSelectedItems(); + + // Export svg + const guideLayers = hideGuideLayers(true /* includeRaster */); + const bounds = paper.project.activeLayer.bounds; + const svg = paper.project.exportSVG({ + bounds: 'content', + matrix: new paper.Matrix().translate(-bounds.x, -bounds.y) + }); + showGuideLayers(guideLayers); + + // Get rid of anti-aliasing + // @todo get crisp text? + svg.setAttribute('shape-rendering', 'crispEdges'); + inlineSvgFonts(svg); + const svgString = (new XMLSerializer()).serializeToString(svg); + + // Put anti-aliased SVG into image, and dump image back into canvas + const img = new Image(); + img.onload = () => { + if (img.width && img.height) { + getRaster().drawImage( + img, + new paper.Point(Math.floor(bounds.topLeft.x), Math.floor(bounds.topLeft.y))); + } + paper.project.activeLayer.removeChildren(); + onUpdateImage(); + }; + img.onerror = () => { + // Fallback if browser does not support SVG data URIs in images. + // The problem with rasterize is that it will anti-alias. + const raster = paper.project.activeLayer.rasterize(72, false /* insert */); + raster.onLoad = () => { + if (raster.canvas.width && raster.canvas.height) { + getRaster().drawImage(raster.canvas, raster.bounds.topLeft); + } + paper.project.activeLayer.removeChildren(); + onUpdateImage(); + }; + }; + // Hash tags will break image loading without being encoded first + img.src = `data:image/svg+xml;utf8,${encodeURIComponent(svgString)}`; +}; + +const convertToVector = function (clearSelectedItems, onUpdateImage) { + clearSelectedItems(); + const trimmedRaster = trim_(getRaster()); + if (trimmedRaster) { + paper.project.activeLayer.addChild(trimmedRaster); + } + clearRaster(); + onUpdateImage(); +}; + export { + convertToBitmap, + convertToVector, getBrushMark, getHitBounds, fillEllipse, - forEachLinePoint, - trim + forEachLinePoint };