diff --git a/src/components/bit-rect-mode/bit-rect-mode.jsx b/src/components/bit-rect-mode/bit-rect-mode.jsx
index 42c58c33..c69c4ddf 100644
--- a/src/components/bit-rect-mode/bit-rect-mode.jsx
+++ b/src/components/bit-rect-mode/bit-rect-mode.jsx
@@ -1,27 +1,26 @@
import React from 'react';
+import PropTypes from 'prop-types';
-import {ComingSoonTooltip} from '../coming-soon/coming-soon.jsx';
import ToolSelectComponent from '../tool-select-base/tool-select-base.jsx';
import rectIcon from './rectangle.svg';
-const BitRectComponent = () => (
-
-
-
+const BitRectComponent = props => (
+
);
+BitRectComponent.propTypes = {
+ isSelected: PropTypes.bool.isRequired,
+ onMouseDown: PropTypes.func.isRequired
+};
+
export default BitRectComponent;
diff --git a/src/components/paint-editor/paint-editor.jsx b/src/components/paint-editor/paint-editor.jsx
index 387e2cb6..f2ad64f6 100644
--- a/src/components/paint-editor/paint-editor.jsx
+++ b/src/components/paint-editor/paint-editor.jsx
@@ -9,7 +9,7 @@ import PaperCanvas from '../../containers/paper-canvas.jsx';
import BitBrushMode from '../../containers/bit-brush-mode.jsx';
import BitLineMode from '../../containers/bit-line-mode.jsx';
import BitOvalMode from '../../components/bit-oval-mode/bit-oval-mode.jsx';
-import BitRectMode from '../../components/bit-rect-mode/bit-rect-mode.jsx';
+import BitRectMode from '../../containers/bit-rect-mode.jsx';
import BitTextMode from '../../components/bit-text-mode/bit-text-mode.jsx';
import BitFillMode from '../../components/bit-fill-mode/bit-fill-mode.jsx';
import BitEraserMode from '../../components/bit-eraser-mode/bit-eraser-mode.jsx';
@@ -177,7 +177,9 @@ const PaintEditorComponent = props => (
onUpdateImage={props.onUpdateImage}
/>
-
+
diff --git a/src/containers/bit-rect-mode.jsx b/src/containers/bit-rect-mode.jsx
new file mode 100644
index 00000000..ea46843e
--- /dev/null
+++ b/src/containers/bit-rect-mode.jsx
@@ -0,0 +1,109 @@
+import paper from '@scratch/paper';
+import PropTypes from 'prop-types';
+import React from 'react';
+import {connect} from 'react-redux';
+import bindAll from 'lodash.bindall';
+import Modes from '../lib/modes';
+import {MIXED} from '../helper/style-path';
+
+import {changeFillColor, DEFAULT_COLOR} from '../reducers/fill-color';
+import {changeMode} from '../reducers/modes';
+import {clearSelectedItems, setSelectedItems} from '../reducers/selected-items';
+import {clearSelection, getSelectedLeafItems} from '../helper/selection';
+import RectTool from '../helper/bit-tools/rect-tool';
+import RectModeComponent from '../components/bit-rect-mode/bit-rect-mode.jsx';
+
+class BitRectMode extends React.Component {
+ constructor (props) {
+ super(props);
+ bindAll(this, [
+ 'activateTool',
+ 'deactivateTool'
+ ]);
+ }
+ componentDidMount () {
+ if (this.props.isRectModeActive) {
+ this.activateTool(this.props);
+ }
+ }
+ componentWillReceiveProps (nextProps) {
+ if (this.tool && nextProps.color !== this.props.color) {
+ this.tool.setColor(nextProps.color);
+ }
+ if (this.tool && nextProps.selectedItems !== this.props.selectedItems) {
+ this.tool.onSelectionChanged(nextProps.selectedItems);
+ }
+
+ if (nextProps.isRectModeActive && !this.props.isRectModeActive) {
+ this.activateTool();
+ } else if (!nextProps.isRectModeActive && this.props.isRectModeActive) {
+ this.deactivateTool();
+ }
+ }
+ shouldComponentUpdate (nextProps) {
+ return nextProps.isRectModeActive !== this.props.isRectModeActive;
+ }
+ activateTool () {
+ clearSelection(this.props.clearSelectedItems);
+ // Force the default brush color if fill is MIXED or transparent
+ const fillColorPresent = this.props.color !== MIXED && this.props.color !== null;
+ if (!fillColorPresent) {
+ this.props.onChangeFillColor(DEFAULT_COLOR);
+ }
+ this.tool = new RectTool(
+ this.props.setSelectedItems,
+ this.props.clearSelectedItems,
+ this.props.onUpdateImage);
+ this.tool.setColor(this.props.color);
+ this.tool.activate();
+ }
+ deactivateTool () {
+ this.tool.deactivateTool();
+ this.tool.remove();
+ this.tool = null;
+ }
+ render () {
+ return (
+
+ );
+ }
+}
+
+BitRectMode.propTypes = {
+ clearSelectedItems: PropTypes.func.isRequired,
+ color: PropTypes.string,
+ handleMouseDown: PropTypes.func.isRequired,
+ isRectModeActive: PropTypes.bool.isRequired,
+ onChangeFillColor: PropTypes.func.isRequired,
+ onUpdateImage: PropTypes.func.isRequired,
+ selectedItems: PropTypes.arrayOf(PropTypes.instanceOf(paper.Item)),
+ setSelectedItems: PropTypes.func.isRequired
+};
+
+const mapStateToProps = state => ({
+ color: state.scratchPaint.color.fillColor,
+ isRectModeActive: state.scratchPaint.mode === Modes.BIT_RECT,
+ selectedItems: state.scratchPaint.selectedItems
+});
+const mapDispatchToProps = dispatch => ({
+ clearSelectedItems: () => {
+ dispatch(clearSelectedItems());
+ },
+ setSelectedItems: () => {
+ dispatch(setSelectedItems(getSelectedLeafItems()));
+ },
+ handleMouseDown: () => {
+ dispatch(changeMode(Modes.BIT_RECT));
+ },
+ onChangeFillColor: fillColor => {
+ dispatch(changeFillColor(fillColor));
+ }
+});
+
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(BitRectMode);
diff --git a/src/containers/paint-editor.jsx b/src/containers/paint-editor.jsx
index 8460845c..c5b6745e 100644
--- a/src/containers/paint-editor.jsx
+++ b/src/containers/paint-editor.jsx
@@ -2,6 +2,7 @@ import paper from '@scratch/paper';
import PropTypes from 'prop-types';
import React from 'react';
+import {connect} from 'react-redux';
import PaintEditorComponent from '../components/paint-editor/paint-editor.jsx';
import {changeMode} from '../reducers/modes';
@@ -13,7 +14,7 @@ import {setTextEditTarget} from '../reducers/text-edit-target';
import {updateViewBounds} from '../reducers/view-bounds';
import {getRaster, hideGuideLayers, showGuideLayers} from '../helper/layer';
-import {getHitBounds} from '../helper/bitmap';
+import {convertToBitmap, convertToVector, getHitBounds} from '../helper/bitmap';
import {performUndo, performRedo, performSnapshot, shouldShowUndo, shouldShowRedo} from '../helper/undo';
import {bringToFront, sendBackward, sendToBack, bringForward} from '../helper/order';
import {groupSelection, ungroupSelection} from '../helper/group';
@@ -24,9 +25,9 @@ import {resetZoom, zoomOnSelection} from '../helper/view';
import EyeDropperTool from '../helper/tools/eye-dropper';
import Modes from '../lib/modes';
+import {BitmapModes} from '../lib/modes';
import Formats from '../lib/format';
import {isBitmap, isVector} from '../lib/format';
-import {connect} from 'react-redux';
import bindAll from 'lodash.bindall';
class PaintEditor extends React.Component {
@@ -61,6 +62,9 @@ class PaintEditor extends React.Component {
canvas: null,
colorInfo: null
};
+ // When isSwitchingFormats is true, the format is about to switch, but isn't done switching.
+ // This gives currently active tools a chance to finish what they were doing.
+ this.isSwitchingFormats = false;
}
componentDidMount () {
document.addEventListener('keydown', (/* event */) => {
@@ -76,6 +80,17 @@ class PaintEditor extends React.Component {
document.addEventListener('mousedown', this.onMouseDown);
document.addEventListener('touchstart', this.onMouseDown);
}
+ componentWillReceiveProps (newProps) {
+ if ((isVector(this.props.format) && newProps.format === Formats.BITMAP) ||
+ (isBitmap(this.props.format) && newProps.format === Formats.VECTOR)) {
+ this.isSwitchingFormats = true;
+ }
+ if (isVector(this.props.format) && isBitmap(newProps.format)) {
+ this.switchMode(Formats.BITMAP);
+ } else if (isVector(newProps.format) && isBitmap(this.props.format)) {
+ this.switchMode(Formats.VECTOR);
+ }
+ }
componentDidUpdate (prevProps) {
if (this.props.isEyeDropping && !prevProps.isEyeDropping) {
this.startEyeDroppingLoop();
@@ -83,9 +98,12 @@ class PaintEditor extends React.Component {
this.stopEyeDroppingLoop();
}
- if ((isVector(this.props.format) && isBitmap(prevProps.format)) ||
- (isVector(prevProps.format) && isBitmap(this.props.format))) {
- this.switchMode(this.props.format);
+ if (this.props.format === Formats.VECTOR && isBitmap(prevProps.format)) {
+ this.isSwitchingFormats = false;
+ convertToVector(this.props.clearSelectedItems, this.handleUpdateImage);
+ } else if (isVector(prevProps.format) && this.props.format === Formats.BITMAP) {
+ this.isSwitchingFormats = false;
+ convertToBitmap(this.props.clearSelectedItems, this.handleUpdateImage);
}
}
componentWillUnmount () {
@@ -103,6 +121,9 @@ class PaintEditor extends React.Component {
case Modes.BIT_LINE:
this.props.changeMode(Modes.LINE);
break;
+ case Modes.BIT_RECT:
+ this.props.changeMode(Modes.RECT);
+ break;
default:
this.props.changeMode(Modes.BRUSH);
}
@@ -114,20 +135,28 @@ class PaintEditor extends React.Component {
case Modes.LINE:
this.props.changeMode(Modes.BIT_LINE);
break;
+ case Modes.RECT:
+ this.props.changeMode(Modes.BIT_RECT);
+ break;
default:
this.props.changeMode(Modes.BIT_BRUSH);
}
}
}
handleUpdateImage (skipSnapshot) {
- if (isBitmap(this.props.format)) {
+ // If in the middle of switching formats, rely on the current mode instead of format.
+ let actualFormat = this.props.format;
+ if (this.isSwitchingFormats) {
+ actualFormat = BitmapModes[this.props.mode] ? Formats.BITMAP : Formats.VECTOR;
+ }
+ if (isBitmap(actualFormat)) {
const rect = getHitBounds(getRaster());
this.props.onUpdateImage(
false /* isVector */,
getRaster().getImageData(rect),
(ART_BOARD_WIDTH / 2) - rect.x,
(ART_BOARD_HEIGHT / 2) - rect.y);
- } else if (isVector(this.props.format)) {
+ } else if (isVector(actualFormat)) {
const guideLayers = hideGuideLayers(true /* includeRaster */);
// Export at 0.5x
@@ -150,7 +179,7 @@ class PaintEditor extends React.Component {
}
if (!skipSnapshot) {
- performSnapshot(this.props.undoSnapshot, this.props.format);
+ performSnapshot(this.props.undoSnapshot, actualFormat);
}
}
handleUndo () {
diff --git a/src/containers/paper-canvas.jsx b/src/containers/paper-canvas.jsx
index 0f5075e6..8df84571 100644
--- a/src/containers/paper-canvas.jsx
+++ b/src/containers/paper-canvas.jsx
@@ -7,7 +7,6 @@ import Formats from '../lib/format';
import Modes from '../lib/modes';
import log from '../log/log';
-import {convertToBitmap, convertToVector} from '../helper/bitmap';
import {performSnapshot} from '../helper/undo';
import {undoSnapshot, clearUndoState} from '../reducers/undo';
import {isGroup, ungroupItems} from '../helper/group';
@@ -21,8 +20,6 @@ import {clearPasteOffset} from '../reducers/clipboard';
import {updateViewBounds} from '../reducers/view-bounds';
import {changeFormat} from '../reducers/format';
-import {isVector, isBitmap} from '../lib/format';
-
import styles from './paper-canvas.css';
class PaperCanvas extends React.Component {
@@ -56,10 +53,6 @@ class PaperCanvas extends React.Component {
if (this.props.imageId !== newProps.imageId) {
this.switchCostume(
newProps.imageFormat, newProps.image, newProps.rotationCenterX, newProps.rotationCenterY);
- } else if (isVector(this.props.format) && newProps.format === Formats.BITMAP) {
- convertToBitmap(this.props.clearSelectedItems, this.props.onUpdateImage);
- } else if (isBitmap(this.props.format) && newProps.format === Formats.VECTOR) {
- convertToVector(this.props.clearSelectedItems, this.props.onUpdateImage);
}
}
componentWillUnmount () {
@@ -260,12 +253,11 @@ PaperCanvas.propTypes = {
clearPasteOffset: PropTypes.func.isRequired,
clearSelectedItems: PropTypes.func.isRequired,
clearUndo: PropTypes.func.isRequired,
- format: PropTypes.oneOf(Object.keys(Formats)), // Internal, up-to-date data format
image: PropTypes.oneOfType([
PropTypes.string,
PropTypes.instanceOf(HTMLImageElement)
]),
- imageFormat: PropTypes.string, // The incoming image's data format, used during import
+ imageFormat: PropTypes.string, // The incoming image's data format, used during import. The user could switch this.
imageId: PropTypes.string,
mode: PropTypes.oneOf(Object.keys(Modes)),
onUpdateImage: PropTypes.func.isRequired,
diff --git a/src/helper/bit-tools/rect-tool.js b/src/helper/bit-tools/rect-tool.js
new file mode 100644
index 00000000..49f0cfc6
--- /dev/null
+++ b/src/helper/bit-tools/rect-tool.js
@@ -0,0 +1,152 @@
+import paper from '@scratch/paper';
+import Modes from '../../lib/modes';
+import {drawRect} from '../bitmap';
+import {getRaster} from '../layer';
+import {clearSelection} from '../selection';
+import BoundingBoxTool from '../selection-tools/bounding-box-tool';
+import NudgeTool from '../selection-tools/nudge-tool';
+
+/**
+ * Tool for drawing rects.
+ */
+class RectTool extends paper.Tool {
+ static get TOLERANCE () {
+ return 6;
+ }
+ /**
+ * @param {function} setSelectedItems Callback to set the set of selected items in the Redux state
+ * @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state
+ * @param {!function} onUpdateImage A callback to call when the image visibly changes
+ */
+ constructor (setSelectedItems, clearSelectedItems, onUpdateImage) {
+ super();
+ this.setSelectedItems = setSelectedItems;
+ this.clearSelectedItems = clearSelectedItems;
+ this.onUpdateImage = onUpdateImage;
+ this.boundingBoxTool = new BoundingBoxTool(Modes.BIT_RECT, setSelectedItems, clearSelectedItems, onUpdateImage);
+ const nudgeTool = new NudgeTool(this.boundingBoxTool, onUpdateImage);
+
+ // We have to set these functions instead of just declaring them because
+ // paper.js tools hook up the listeners in the setter functions.
+ this.onMouseDown = this.handleMouseDown;
+ this.onMouseDrag = this.handleMouseDrag;
+ this.onMouseUp = this.handleMouseUp;
+ this.onKeyUp = nudgeTool.onKeyUp;
+ this.onKeyDown = nudgeTool.onKeyDown;
+
+ this.rect = null;
+ this.color = null;
+ this.active = false;
+ }
+ getHitOptions () {
+ return {
+ segments: false,
+ stroke: true,
+ curves: false,
+ fill: true,
+ guide: false,
+ match: hitResult =>
+ (hitResult.item.data && hitResult.item.data.isHelperItem) ||
+ hitResult.item === this.rect, // Allow hits on bounding box and rect only
+ tolerance: RectTool.TOLERANCE / paper.view.zoom
+ };
+ }
+ /**
+ * Should be called if the selection changes to update the bounds of the bounding box.
+ * @param {Array} selectedItems Array of selected items.
+ */
+ onSelectionChanged (selectedItems) {
+ this.boundingBoxTool.onSelectionChanged(selectedItems);
+ if ((!this.rect || !this.rect.parent) &&
+ selectedItems && selectedItems.length === 1 && selectedItems[0].shape === 'rectangle') {
+ // Infer that an undo occurred and get back the active rect
+ this.rect = selectedItems[0];
+ } else if (this.rect && this.rect.parent && !this.rect.selected) {
+ // Rectangle got deselected
+ this.commitRect();
+ }
+ }
+ setColor (color) {
+ this.color = color;
+ }
+ handleMouseDown (event) {
+ if (event.event.button > 0) return; // only first mouse button
+ this.active = true;
+
+ if (this.boundingBoxTool.onMouseDown(event, false /* clone */, false /* multiselect */, this.getHitOptions())) {
+ this.isBoundingBoxMode = true;
+ } else {
+ this.isBoundingBoxMode = false;
+ clearSelection(this.clearSelectedItems);
+ this.commitRect();
+ }
+ }
+ handleMouseDrag (event) {
+ if (event.event.button > 0 || !this.active) return; // only first mouse button
+
+ if (this.isBoundingBoxMode) {
+ this.boundingBoxTool.onMouseDrag(event);
+ return;
+ }
+
+ const dimensions = event.point.subtract(event.downPoint);
+ const baseRect = new paper.Rectangle(event.downPoint, event.point);
+ if (event.modifiers.shift) {
+ baseRect.height = baseRect.width;
+ dimensions.y = event.downPoint.y > event.point.y ? -Math.abs(baseRect.width) : Math.abs(baseRect.width);
+ }
+ if (this.rect) this.rect.remove();
+ this.rect = new paper.Shape.Rectangle(baseRect);
+ this.rect.fillColor = this.color;
+
+ if (event.modifiers.alt) {
+ this.rect.position = event.downPoint;
+ } else {
+ this.rect.position = event.downPoint.add(dimensions.multiply(.5));
+ }
+ }
+ handleMouseUp (event) {
+ if (event.event.button > 0 || !this.active) return; // only first mouse button
+
+ if (this.isBoundingBoxMode) {
+ this.boundingBoxTool.onMouseUp(event);
+ this.isBoundingBoxMode = null;
+ return;
+ }
+
+ if (this.rect) {
+ if (Math.abs(this.rect.size.width * this.rect.size.height) < RectTool.TOLERANCE / paper.view.zoom) {
+ // Tiny shape created unintentionally?
+ this.rect.remove();
+ this.rect = null;
+ } else {
+ // Hit testing does not work correctly unless the width and height are positive
+ this.rect.size = new paper.Point(Math.abs(this.rect.size.width), Math.abs(this.rect.size.height));
+ this.rect.selected = true;
+ this.setSelectedItems();
+ }
+ }
+ this.active = false;
+ }
+ commitRect () {
+ if (!this.rect || !this.rect.parent) return;
+
+ const tmpCanvas = document.createElement('canvas');
+ tmpCanvas.width = getRaster().width;
+ tmpCanvas.height = getRaster().height;
+ const context = tmpCanvas.getContext('2d');
+ context.fillStyle = this.color;
+ drawRect(this.rect, context);
+ getRaster().drawImage(tmpCanvas, new paper.Point());
+
+ this.rect.remove();
+ this.rect = null;
+ this.onUpdateImage();
+ }
+ deactivateTool () {
+ this.commitRect();
+ this.boundingBoxTool.removeBoundsPath();
+ }
+}
+
+export default RectTool;
diff --git a/src/helper/bitmap.js b/src/helper/bitmap.js
index 1aac8241..f4eca707 100644
--- a/src/helper/bitmap.js
+++ b/src/helper/bitmap.js
@@ -209,9 +209,135 @@ const convertToVector = function (clearSelectedItems, onUpdateImage) {
onUpdateImage();
};
+const getColor_ = function (x, y, context) {
+ return context.getImageData(x, y, 1, 1).data;
+};
+
+const matchesColor_ = function (x, y, imageData, oldColor) {
+ const index = ((y * imageData.width) + x) * 4;
+ return (
+ imageData.data[index + 0] === oldColor[0] &&
+ imageData.data[index + 1] === oldColor[1] &&
+ imageData.data[index + 2] === oldColor[2] &&
+ imageData.data[index + 3 ] === oldColor[3]
+ );
+};
+
+const colorPixel_ = function (x, y, imageData, newColor) {
+ const index = ((y * imageData.width) + x) * 4;
+ imageData.data[index + 0] = newColor[0];
+ imageData.data[index + 1] = newColor[1];
+ imageData.data[index + 2] = newColor[2];
+ imageData.data[index + 3] = newColor[3];
+};
+
+/**
+ * Flood fill beginning at the given point.
+ * Based on http://www.williammalone.com/articles/html5-canvas-javascript-paint-bucket-tool/
+ *
+ * @param {!int} x The x coordinate on the context at which to begin
+ * @param {!int} y The y coordinate on the context at which to begin
+ * @param {!ImageData} imageData The image data to edit
+ * @param {!Array} newColor The color to replace with. A length 4 array [r, g, b, a].
+ * @param {!Array} oldColor The color to replace. A length 4 array [r, g, b, a].
+ * This must be different from newColor.
+ * @param {!Array>} stack The stack of pixels we need to look at
+ */
+const floodFillInternal_ = function (x, y, imageData, newColor, oldColor, stack) {
+ while (y > 0 && matchesColor_(x, y - 1, imageData, oldColor)) {
+ y--;
+ }
+ let lastLeftMatchedColor = false;
+ let lastRightMatchedColor = false;
+ for (; y < imageData.height; y++) {
+ if (!matchesColor_(x, y, imageData, oldColor)) break;
+ colorPixel_(x, y, imageData, newColor);
+ if (x > 0) {
+ if (matchesColor_(x - 1, y, imageData, oldColor)) {
+ if (!lastLeftMatchedColor) {
+ stack.push([x - 1, y]);
+ lastLeftMatchedColor = true;
+ }
+ } else {
+ lastLeftMatchedColor = false;
+ }
+ }
+ if (x < imageData.width - 1) {
+ if (matchesColor_(x + 1, y, imageData, oldColor)) {
+ if (!lastRightMatchedColor) {
+ stack.push([x + 1, y]);
+ lastRightMatchedColor = true;
+ }
+ } else {
+ lastRightMatchedColor = false;
+ }
+ }
+ }
+};
+
+/**
+ * Flood fill beginning at the given point
+ * @param {!int} x The x coordinate on the context at which to begin
+ * @param {!int} y The y coordinate on the context at which to begin
+ * @param {!HTMLCanvas2DContext} context The context in which to draw
+ */
+const floodFill = function (x, y, context) {
+ const oldColor = getColor_(x, y, context);
+ context.fillRect(x, y, 1, 1);
+ const newColor = getColor_(x, y, context);
+ const imageData = context.getImageData(0, 0, context.canvas.width, context.canvas.height);
+ if (matchesColor_(x, y, imageData, oldColor)) { // no-op
+ return;
+ }
+ colorPixel_(x, y, imageData, newColor); // Restore old color to avoid affecting result
+ const stack = [[x, y]];
+ while (stack.length) {
+ const pop = stack.pop();
+ floodFillInternal_(pop[0], pop[1], imageData, newColor, oldColor, stack);
+ }
+ context.putImageData(imageData, 0, 0);
+};
+
+/**
+ * @param {!paper.Shape.Rectangle} rect The rectangle to draw to the canvas
+ * @param {!HTMLCanvas2DContext} context The context in which to draw
+ */
+const drawRect = function (rect, context) {
+ // No rotation component to matrix
+ if (rect.matrix.b === 0 && rect.matrix.c === 0) {
+ const width = rect.size.width * rect.matrix.a;
+ const height = rect.size.height * rect.matrix.d;
+ context.fillRect(
+ ~~(rect.matrix.tx - (width / 2)),
+ ~~(rect.matrix.ty - (height / 2)),
+ ~~width,
+ ~~height);
+ return;
+ }
+ const startPoint = rect.matrix.transform(new paper.Point(-rect.size.width / 2, -rect.size.height / 2));
+ const widthPoint = rect.matrix.transform(new paper.Point(rect.size.width / 2, -rect.size.height / 2));
+ const heightPoint = rect.matrix.transform(new paper.Point(-rect.size.width / 2, rect.size.height / 2));
+ const endPoint = rect.matrix.transform(new paper.Point(rect.size.width / 2, rect.size.height / 2));
+ const center = rect.matrix.transform(new paper.Point());
+ forEachLinePoint(startPoint, widthPoint, (x, y) => {
+ context.fillRect(x, y, 1, 1);
+ });
+ forEachLinePoint(startPoint, heightPoint, (x, y) => {
+ context.fillRect(x, y, 1, 1);
+ });
+ forEachLinePoint(endPoint, widthPoint, (x, y) => {
+ context.fillRect(x, y, 1, 1);
+ });
+ forEachLinePoint(endPoint, heightPoint, (x, y) => {
+ context.fillRect(x, y, 1, 1);
+ });
+ floodFill(~~center.x, ~~center.y, context);
+};
+
export {
convertToBitmap,
convertToVector,
+ drawRect,
getBrushMark,
getHitBounds,
fillEllipse,
diff --git a/src/lib/modes.js b/src/lib/modes.js
index 332558cd..e0e25b2a 100644
--- a/src/lib/modes.js
+++ b/src/lib/modes.js
@@ -3,6 +3,7 @@ import keyMirror from 'keymirror';
const Modes = keyMirror({
BIT_BRUSH: null,
BIT_LINE: null,
+ BIT_RECT: null,
BRUSH: null,
ERASER: null,
LINE: null,
@@ -15,4 +16,13 @@ const Modes = keyMirror({
TEXT: null
});
-export default Modes;
+const BitmapModes = keyMirror({
+ BIT_BRUSH: null,
+ BIT_LINE: null,
+ BIT_RECT: null
+});
+
+export {
+ Modes as default,
+ BitmapModes
+};