This commit is contained in:
adrianvic 2023-12-21 14:33:34 -03:00
commit 59426d62fb
102 changed files with 42796 additions and 0 deletions

View file

@ -0,0 +1,17 @@
.Canvas
{
margin-left: 100px;
margin-top: 100px;
}
#paintCanvas
{
background: rgb(242, 242, 222);
height: 100%;
width: 100%;
}
.pannable
{
overflow-x: scroll !important;
}

View file

@ -0,0 +1,33 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=10" />
<title>Canvas</title>
<link rel="stylesheet" href="Canvas.css" />
<script src="/WinJS/js/base.js"></script>
<script src="/JS/Global.js"></script>
<!-- canvas control files -->
<script src="/Controls/Paint/Math/Point.js"></script>
<script src="/Controls/Paint/Math/Vector.js"></script>
<script src="/Controls/Paint/Graphics/Canvas.js"></script>
<script src="/Controls/Paint/Graphics/Color.js"></script>
<script src="/Controls/Paint/Graphics/Path.js"></script>
<script src="/Controls/Paint/Graphics/Texture.js"></script>
<script src="/Controls/Paint/Tools/Crayon.js"></script>
<script src="/Controls/Paint/Tools/Marker.js"></script>
<script src="/Controls/Paint/Tools/Neon.js"></script>
<script src="/Controls/Paint/Tools/Eraser.js"></script>
<script src="/Controls/Paint/Tools/Bristle.js"></script>
<script src="/Controls/Paint/Tools/Brush.js"></script>
<script src="/Controls/Paint/Tools/Pipe.js"></script>
<script src="/Controls/Paint/Painter.js"></script>
<script src="ToolCards.js"></script>
<script src="Canvas.js"></script>
</head>
<body>
<canvas id="paintCanvas"></canvas>
</body>
</html>

493
Controls/Canvas/Canvas.js Normal file
View file

@ -0,0 +1,493 @@
////////////////////////////////////////////////////////////
//// © Microsoft. All rights reserved. ////
////////////////////////////////////////////////////////////
/// <reference path="Paint.js" />
/// <reference path="Painter.js" />
(function (AppNS) {
"use strict";
var WinUtils = WinJS.Utilities;
// Enum of pointer event information
var pointerInfo = {
// Pointer type
touch: 2,
pen: 3,
mouse: 4,
// Button pressed
noButtons: 0,
leftButton: 1,
rightButton: 2,
middleButton: 4,
};
var layoutState = Windows.UI.ViewManagement.ApplicationLayoutState;
function fragmentLoad(root, state) {
// Set up canvas dimensions
var canvas = root.querySelector("#paintCanvas");
canvas.height = state.canvasHeight - state.canvasBorder;
canvas.style.height = canvas.height + "px";
canvas.width = state.canvasWidth - state.canvasBorder * 2;
canvas.style.width = canvas.width + "px";
var eventHost = document.createElement("div");
var pointerDown = [];
var Painter = AppNS.Painter;
var mainPainter = new Painter.Painter(canvas);
var canvasBlocked = false;
var classes = { pannable: "pannable" };
var undoBlobPrefix = "undoBlob-";
// Initialize canvas with saved blob
if (state.canvasBlobName) {
Windows.Storage.ApplicationData.current.localFolder.getFileAsync(state.canvasBlobName)
.then(function (file) {
var url = URL.createObjectURL(file, false);
var img = document.createElement("img");
img.addEventListener("load", function () {
canvas.getContext("2d").drawImage(img, 0, 0);
});
img.src = url;
});
}
// Initialize undo stack
if (state.undoBlobNames) {
for (var i = 0, len = state.undoBlobNames.length; i < len; i++) {
var blobName = state.undoBlobNames[i];
Windows.Storage.ApplicationData.current.localFolder.getFileAsync(blobName)
.then(function (file) {
var url = URL.createObjectURL(file, false);
var img = document.createElement("img");
var tempCanvas = document.createElement("canvas");
tempCanvas.height = canvas.height;
tempCanvas.width = canvas.width;
img.addEventListener("load", function () {
var context = tempCanvas.getContext("2d");
context.drawImage(img, 0, 0);
mainPainter.canvas.history.push(context.getImageData(0, 0, mainPainter.canvas.domCanvas.offsetWidth, mainPainter.canvas.domCanvas.offsetHeight));
mainPainter.canvas.maxIndex++;
mainPainter.canvas.index++;
});
img.src = url;
});
}
}
AppNS.CanvasManager.loadAllTools(mainPainter);
// Initialize canvas
var toolName = state.toolName || AppNS.CanvasManager.ToolNames.Marker;
var toolCard = AppNS.CanvasManager.ToolCards[toolName];
mainPainter.toolName = toolName;
mainPainter.tool.width = state.width || toolCard.supportedSizes[toolCard.defaultSizeIndex];
if (state.toolOptionValueIndex || state.toolOptionValueIndex === 0) {
mainPainter.tool[toolCard.optionKey] = toolCard.optionValues[state.toolOptionValueIndex];
}
function downListener(event) {
if (!pointerDown[event.pointerId] && (leftClick(event) || notMouse(event)) && !canvasBlocked) {
mainPainter.drawStart(dataFromEvent(event));
pointerDown[event.pointerId] = true;
}
}
function moveListener(event) {
if (pointerDown[event.pointerId] && !canvasBlocked) {
mainPainter.draw(dataFromEvent(event));
}
}
function upListener(event) {
if (pointerDown[event.pointerId] && !canvasBlocked) {
mainPainter.drawStop(dataFromEvent(event));
AppNS.ToolbarManager.showUndo();
pointerDown[event.pointerId] = false;
}
}
function dataFromEvent(event) {
var data = {};
data.x = event.offsetX;
data.y = event.offsetY;
data.id = event.pointerId;
return data;
}
function notMouse(event) {
return (event.pointerType !== pointerInfo.mouse);
}
function leftClick(event) {
return (event.pointerType === pointerInfo.mouse && event.button === pointerInfo.leftButton);
}
// Event listeners for the canvas
canvas.addEventListener("MSPointerDown", downListener);
canvas.addEventListener("MSPointerOver", downListener);
canvas.addEventListener("MSPointerMove", moveListener);
canvas.addEventListener("MSPointerOut", upListener);
canvas.addEventListener("MSPointerUp", upListener);
WinJS.Namespace.defineWithParent(AppNS, "CanvasManager", {
color: {
get: function () {
/// <summary>
/// Gets the current color string value
/// </summary>
return mainPainter.color.toString();
},
set: function (newColor) {
/// <summary>
/// Sets the current color string value
/// </summary>
/// <param name='newColor'>
/// The string value for the color
/// </param>
var color = new Painter.Graphics.Color();
color.fromString(newColor);
mainPainter.color = color;
}
},
clearCanvas: function () {
/// <summary>
/// Resets the canvas to a blank state
/// </summary>
mainPainter.canvas.clear();
AppNS.ToolbarManager.showUndo();
},
undoCanvas: function () {
/// <summary>
/// Undoes the last stroke
/// </summary>
if (mainPainter.canvas.canUndo()) {
mainPainter.canvas.undo();
}
AppNS.ToolbarManager.hideUndo();
},
canUndo: function () {
/// <summary>
/// Returns whether or not another undo action can be executed
/// </summary>
/// <return>
/// Boolean representing whether or not another undo action can be executed
/// </return>
return mainPainter.canvas.canUndo();
},
redoCanvas: function () {
/// <summary>
/// Redoes the last stroke
/// </summary>
if (mainPainter.canvas.canRedo()) {
mainPainter.canvas.redo();
}
AppNS.ToolbarManager.showUndo();
},
toolWidth: {
get: function () {
/// <summary>
/// Sets current tool
/// </summary>
/// <param name='newBrush'>
/// The string value for the tool
/// </param>
return mainPainter.tool.width;
},
set: function (newWidth) {
/// <summary>
/// Sets the current tool width
/// </summary>
/// <param name='newWidth'>
/// The width value in pixels
/// </param>
mainPainter.tool.width = newWidth;
}
},
getToolProperty: function (optionKey) {
/// <summary>
/// Gets the tool specific property
/// </summary>
/// <param name='optionKey'>
/// The property key to be returned
/// </param>
/// <return>
/// The value currently assigned to the property. Can be any type.
/// </return>
return mainPainter.tool[optionKey];
},
setToolProperty: function (optionKey, optionValue) {
/// <summary>
/// Sets the tool specific property
/// </summary>
/// <param name='optionKey'>
/// The property key to be set
/// </param>
/// <param name='optionValue'>
/// The value to be set
/// </param>
mainPainter.tool[optionKey] = optionValue;
},
tool: {
get: function () {
/// <summary>
/// Returns the current tool
/// </summary>
/// <returns>
/// Tool
/// </returns>
return mainPainter.tool;
},
set: function (newBrush) {
/// <summary>
/// Sets current tool
/// </summary>
/// <param name='newBrush'>
/// The string value for the tool
/// </param>
mainPainter.tool = newBrush;
}
},
canvasBitmap: {
get: function () {
/// <summary>
/// Returns the current image data on the canvas
/// </summary>
/// <returns>
/// ImageData
/// </returns>
return mainPainter.canvas.context.getImageData(0, 0, mainPainter.canvas.domCanvas.offsetWidth, mainPainter.canvas.domCanvas.offsetHeight);
},
set: function (imageData) {
/// <summary>
/// Sets the image data displayed on the canvas
/// </summary>
/// <param name='imageData'>
/// ImageData object to be displayed
/// </param>
mainPainter.canvas.context.putImageData(imageData, 0, 0);
}
},
getScaledCanvasBlob: function (height, width) {
/// <summary>
/// Returns the a scaled canvas blob
/// </summary>
/// <param name='height'>
/// Height of the new canvas blob
/// </param>
/// <param name='width'>
/// Width of the new canvas blob
/// </param>
/// <returns>
/// Blob
/// </returns>
var newCanvas = document.createElement("canvas");
var context = newCanvas.getContext("2d");
newCanvas.width = width;
newCanvas.height = height;
context.drawImage(mainPainter.canvas.domCanvas, 0, 0, width, height);
return newCanvas.msToBlob();
},
canvasBlob: {
get: function () {
/// <summary>
/// Returns the current image data on the canvas as a blob
/// </summary>
/// <returns>
/// Blob
/// </returns>
return mainPainter.canvas.domCanvas.msToBlob();
}
},
undoBlobNames: {
get: function () {
/// <summary>
/// Returns an array with the names identifying the undo blobs indexed chronologically
/// </summary>
/// <returns>
/// Array of strings
/// </returns>
var blobNames = [];
for (var i = mainPainter.canvas.minIndex; i < mainPainter.canvas.index; i++) {
blobNames.push(undoBlobPrefix + (i - mainPainter.canvas.minIndex));
}
return blobNames;
}
},
undoBlobs: {
get: function () {
/// <summary>
/// Returns an object with the undo stack blobs
/// </summary>
/// <returns>
/// Object in which keys are the blob names strings and values are blobs
/// </returns>
var blobs = {};
for (var i = mainPainter.canvas.minIndex; i < mainPainter.canvas.index; i++) {
var tempCanvas = document.createElement("canvas");
tempCanvas.height = mainPainter.canvas.domCanvas.height;
tempCanvas.width = mainPainter.canvas.domCanvas.width;
tempCanvas.getContext("2d").putImageData(mainPainter.canvas.history[i], 0, 0);
blobs[undoBlobPrefix + (i - mainPainter.canvas.minIndex)] = tempCanvas.msToBlob();
}
return blobs;
}
},
// Saves the current canvas to a file in the pictures library.
// File name will be 'Painting <date>'.
saveCanvas: function () {
var filename = "Painting.png";
AppNS.Utils.writeBlobToPicturesFolderAsync(this.canvasBlob, filename);
},
canvasBlocked: {
set: function (cBlocked) {
/// <summary>
/// Sets whether the drawing on canvas is disabled or enabled
/// </summary>
/// <param name='cBlocked'>
/// Boolean representing wether or not the canvas is blocked
/// </param>
if (typeof cBlocked === "boolean") {
canvasBlocked = cBlocked;
}
},
get: function () {
/// <summary>
/// Returns whether the drawing on canvas is disabled or enabled
/// </summary>
/// <returns>
/// Boolean
/// </returns>
return canvasBlocked;
}
},
canvasPannable: {
get: function () {
/// <summary>
/// Returns whether or not the canvas is pannable
/// </summary>
/// <returns>
/// Boolean
/// </returns>
return root.className.indexOf(classes.pannable) !== -1;
},
set: function (isPannable) {
/// <summary>
/// Enables or disables panning on the canvas
/// </summary>
/// <param name='isPannable'>
/// Boolean representing wether or not the canvas is pannable
/// </param>
if (isPannable) {
WinUtils.addClass(root, classes.pannable);
} else {
WinUtils.removeClass(root, classes.pannable);
}
}
},
changeLayout: function (newLayout) {
/// <summary>
/// Changes the canvas layout to a specified layout such as snap or fill
/// </summary>
// Reposition scroll for snap and fill view
if (newLayout !== layoutState.fullScreen) {
root.scrollLeft = (canvas.width - window.innerWidth) / 2;
}
},
addEventListener: function (eventName, eventCallback, capture) {
/// <summary>
/// Registers an event handler for the specified event type
/// </summary>
/// <param name='eventName'>
/// The type of event type to register
/// </param>
/// <param name='eventCallback'>
/// The event handler function to associate with the event
/// </param>
/// <param name='capture'>
/// A Boolean value that specifies the event phase to add the event handler for:
/// true
/// Register the event handler for the capturing phase
/// false
/// Register the event handler for the bubbling phase
/// </param>
/// <returns>
/// None
/// </returns>
eventHost.addEventListener(eventName, eventCallback, capture);
},
removeEventListener: function (eventName, eventCallback, capture) {
/// <summary>
/// Removes an event handler that the addEventListener method registered
/// </summary>
/// <param name='eventName'>
/// The event type that the event handler is registered for
/// </param>
/// <param name='eventCallback'>
/// The event handler function to remove
/// </param>
/// <param name='capture'>
/// A Boolean value that specifies the event phase to add the event handler for:
/// true
/// Remove the capturing phase event handler
/// false
/// Remove the bubbling phase event handler
/// </param>
/// <returns>
/// None
/// </returns>
eventHost.removeEventListener(eventName, eventCallback, capture);
},
// Raises an event to listeners
_fireEvent: function (eventName, payload) {
if (document.createEvent) {
var eventObject = document.createEvent("Event");
eventObject.initEvent(eventName, true, false);
if (payload) {
eventObject.payload = payload;
}
eventHost.dispatchEvent(eventObject);
}
}
});
};
WinJS.Namespace.defineWithParent(AppNS, "CanvasManager", {
fragmentLoad: fragmentLoad,
});
})(Microsoft.Paint);

View file

@ -0,0 +1,107 @@
////////////////////////////////////////////////////////////
//// © Microsoft. All rights reserved. ////
////////////////////////////////////////////////////////////
(function (AppNS) {
var supportedSizes = [60, 40, 30, 20, 10, 3];
// Create texture image for crayon
var textureImage = document.createElement("img");
textureImage.src = "../Controls/Paint/Res/crayon_texture.png";
WinJS.Namespace.defineWithParent(AppNS, "CanvasManager", {
ToolNames: {
Eraser: "eraser",
Marker: "marker",
Brush: "brush",
Neon: "neon",
Pipe: "pipe",
Crayon: "crayon"
},
ToolCards: {
marker: {
toolClass: AppNS.Painter.Tools.Marker,
label: "Marker", // TODO: Win8Apps WORK 92: Add localization support
optionKey: "opacity",
optionValues: [1, 0.85, 0.7, 0.5, 0.35, 0.2],
defaultOptionIndex: 1,
defaultSizeIndex: 4,
optionLabel: "Transparency", // TODO: Win8Apps WORK 92: Add localization support
supportedSizes: supportedSizes
},
brush: {
toolClass: AppNS.Painter.Tools.Brush,
label: "Brush", // TODO: Win8Apps WORK 92: Add localization support
optionKey: "fade",
optionValues: [0, 1000, 700, 400, 200, 100],
defaultOptionIndex: 0,
defaultSizeIndex: 3,
optionLabel: "Fade", // TODO: Win8Apps WORK 92: Add localization support
supportedSizes: supportedSizes
},
neon: {
toolClass: AppNS.Painter.Tools.Neon,
label: "Neon", // TODO: Win8Apps WORK 92: Add localization support
optionKey: "glow",
optionValues: [70, 55, 40, 25, 10, 0],
defaultOptionIndex: 4,
defaultSizeIndex: 3,
optionLabel: "Glow", // TODO: Win8Apps WORK 92: Add localization support
supportedSizes: supportedSizes
},
pipe: {
toolClass: AppNS.Painter.Tools.Pipe,
label: "3D", // TODO: Win8Apps WORK 92: Add localization support
optionKey: "height",
optionValues: [6, 5, 4, 3, 2, 1],
defaultOptionIndex: 4,
defaultSizeIndex: 3,
optionLabel: "Depth", // TODO: Win8Apps WORK 92: Add localization support
supportedSizes: supportedSizes
},
crayon: {
toolClass: AppNS.Painter.Tools.Crayon,
label: "Crayon", // TODO: Win8Apps WORK 92: Add localization support
optionKey: "density",
optionValues: [2, 1.75, 1.5, 1.25, 1.0, .75],
defaultOptionIndex: 1,
defaultSizeIndex: 4,
optionLabel: "Density", // TODO: Win8Apps WORK 92: Add localization support
supportedSizes: supportedSizes,
textureImage: textureImage
},
eraser: {
toolClass: AppNS.Painter.Tools.Eraser,
label: "Eraser", // TODO: Win8Apps WORK 92: Add localization support
supportedSizes: supportedSizes,
defaultSizeIndex: 4
}
},
// Loads all available tools into the given painter
loadAllTools: function (painter) {
var cards = AppNS.CanvasManager.ToolCards;
var names = AppNS.CanvasManager.ToolNames;
for (var name in cards) {
var card = cards[name];
var tool = new card.toolClass();
tool.width = card.supportedSizes[card.defaultSizeIndex];
if (name !== names.Eraser) {
tool[card.optionKey] = card.optionValues[card.defaultOptionIndex];
}
painter.loadTool(tool, name);
if (name === names.Crayon) {
// Create texture image for crayon
var textureImage = new Image(40, 40);
var crayonTool = tool;
textureImage.addEventListener("load", function () {
crayonTool.setTexture(textureImage);
});
textureImage.src = "../Controls/Paint/Res/crayon_texture.png";
}
}
}
});
})(Microsoft.Paint);

View file

@ -0,0 +1,28 @@
.colorpicker
{
display: inline;
position: relative;
}
.colorpicker-color
{
padding: 0;
position: absolute;
z-index: 1;
/*
-ms-transition-property: width, height, left, bottom;
-ms-transition-duration: 0.1s;
*/
}
.colorpicker-selectedColor
{
box-shadow: 0px 0px 0px 1px rgb(64, 64, 64), 0px 0px 0px 1px white inset;
z-index: 999;
}
.colorpicker-animatedColor
{
-ms-transition-property: width, height, left, bottom;
-ms-transition-duration: 0.1s;
}

View file

@ -0,0 +1,349 @@
////////////////////////////////////////////////////////////
//// © Microsoft. All rights reserved. ////
////////////////////////////////////////////////////////////
(function (BaseNS) {
var WinUtils = WinJS.Utilities;
WinJS.Namespace.defineWithParent(BaseNS, "Paint", {
// ColorPicker class
ColorPicker: WinJS.Class.define(function (host, colors, options) {
/// <summary>
/// Initialize and render a ColorPicker
/// </summary>
/// <param name='host'>
/// DOM element that will host the color picker
/// </param>
/// <param name='colors'>
/// Array of css-style colors represented as strings
/// </param>
/// <param name='options' optional='true'>
/// Object of options
/// </param>
this._host = host;
this._colors = colors;
this._options = options || {};
this._colorDomElements = [];
this._colorBlockClassName = "colorpicker-color";
this._previousMouseMoveX = false;
// Set up options
this._options.height = this._options.height || 50;
this._options.width = this._options.width || 600;
this._options.separation = this._options.separation || 0;
this._options.selectedHeight = (this._options.selectedHeightGrowth * this._options.height) || this._options.height;
this._colorBlockWidth = this._options.width / this._colors.length;
this._options.selectedWidth = (this._options.selectedWidthGrowth * this._colorBlockWidth) || this._options.colorBlockWidth;
this._options.hoverWidth = (this._options.hoverWidthGrowth * this._colorBlockWidth) || this._options.colorBlockWidth;
this._options.hoverHeight = (this._options.hoverHeightGrowth * this._options.height) || this._options.height;
this._options.bottomPosition = this._options.bottomPosition || 0;
this._options.affectedNeighbors = this._options.affectedNeighbors || 0;
this._direction = { left: 0, right: 1 };
// Create container
this._colorPickerContainer = document.createElement("div");
this._colorPickerContainer.className = "colorpicker";
// Create color block for each color
for (var i = 0, len = this._colors.length; i < len; i++) {
var colorBlock = document.createElement("button");
colorBlock.className = this._colorBlockClassName;
colorBlock.style.height = this._options.height + "px";
colorBlock.style.width = this._colorBlockWidth + "px";
colorBlock.style.left = i * this._colorBlockWidth + "px";
colorBlock.style.bottom = this._options.bottomPosition + "px";
colorBlock.style.minWidth = colorBlock.style.width;
colorBlock.id = i;
if (i !== len - 1) {
colorBlock.style.marginRight = this._options.separation;
}
colorBlock.style.background = this._colors[i];
this._down = false;
var that = this;
// Set up color block event listeners
colorBlock.addEventListener("MSPointerDown", function (event) {
that._pointerDown = true;
if (that._currentSelection) {
that._unselectColorBlock(that._currentSelection);
}
that._expandColorBlock(event.currentTarget, false, true);
that._currentHover = event.currentTarget;
});
colorBlock.addEventListener("MSPointerUp", function (event) {
if (that._pointerDown) {
that._pointerDown = false;
that._unselectColorBlock(that._currentSelection);
that._contractColorBlock(that._currentHover, false, true);
that._selectColorBlock(that._currentHover);
that._currentSelection = that._currentHover;
that._currentHover = null;
that._colorSelected = true;
}
});
colorBlock.addEventListener("mouseover", function (event) {
that._unselectColorBlock(that._currentSelection);
that._selectColorBlock(event.currentTarget);
that._expandColorBlock(event.currentTarget, false, true);
that._currentHover = event.currentTarget;
});
colorBlock.addEventListener("mousemove", function (event) {
var currentId = parseInt(event.currentTarget.id);
if (that._currentHover) {
if (that._currentHover !== event.currentTarget) {
that._unselectColorBlock(that._currentSelection);
if (that._currentHover) {
that._contractColorBlock(that._currentHover, false, true);
}
that._expandColorBlock(event.currentTarget, false, true);
that._currentHover = event.currentTarget;
} else if ((event.offsetX > that._options.hoverWidth - (that._options.hoverWidth / 6) && currentId < that._colors.length - 1) &&
that._previousMouseMoveX && (that._previousMouseMoveX < event.offsetX)) {
that._contractColorBlock(that._currentHover, false, true);
that._expandColorBlock(that._colorDomElements[currentId + 1], false, true);
that._currentHover = that._colorDomElements[currentId + 1];
that._previousMouseMoveX = 0;
} else if ((event.offsetX < that._options.hoverWidth / 6 && currentId > 0) &&
that._previousMouseMoveX && (that._previousMouseMoveX > event.offsetX)) {
that._contractColorBlock(that._currentHover, false, true);
that._expandColorBlock(that._colorDomElements[currentId - 1], false, true);
that._currentHover = that._colorDomElements[currentId - 1];
that._previousMouseMoveX = 0;
} else {
that._previousMouseMoveX = event.offsetX;
}
}
});
colorBlock.addEventListener("MSTransitionEnd", function (event) {
if (parseInt(event.currentTarget.style.height) === that._options.height) { // If it is a contraction transition
if (!this._pointerDown && that._colorSelected) {
that._fireEvent("colorselected", { color: that._currentSelection.style.background, colorId: parseInt(that._currentSelection.id) });
that._colorSelected = false;
}
event.currentTarget.style.zIndex = 1;
}
WinUtils.removeClass(event.currentTarget, "colorpicker-animatedColor");
});
colorBlock.addEventListener("mouseout", function (event) {
if (event.relatedTarget.className.indexOf(that._colorBlockClassName) === -1) {
that._contractColorBlock(event.currentTarget, false, true);
if (that._currentHover) {
that._contractColorBlock(that._currentHover, false, true);
}
if (that._pointerDown) {
that._pointerDown = false;
that._unselectColorBlock(that._currentSelection);
that._unselectColorBlock(that._currentHover);
that._currentSelection = event.currentTarget;
that._fireEvent("colorselected", { color: that._currentSelection.style.background, colorId: parseInt(that._currentSelection.id) });
}
that._selectColorBlock(that._currentSelection);
that._currentHover = null;
}
});
this._colorPickerContainer.appendChild(colorBlock);
this._colorDomElements[i] = colorBlock;
}
host.appendChild(this._colorPickerContainer);
},
{
addEventListener: function (eventName, eventCallback, capture) {
/// <summary>
/// Registers an event handler for the specified event type
/// </summary>
/// <param name='eventName'>
/// The type of event type to register
/// </param>
/// <param name='eventCallback'>
/// The event handler function to associate with the event
/// </param>
/// <param name='capture'>
/// A Boolean value that specifies the event phase to add the event handler for:
/// true
/// Register the event handler for the capturing phase
/// false
/// Register the event handler for the bubbling phase
/// </param>
/// <returns>
/// None
/// </returns>
this._host.addEventListener(eventName, eventCallback, capture);
},
removeEventListener: function (eventName, eventCallback, capture) {
/// <summary>
/// Removes an event handler that the addEventListener method registered
/// </summary>
/// <param name='eventName'>
/// The event type that the event handler is registered for
/// </param>
/// <param name='eventCallback'>
/// The event handler function to remove
/// </param>
/// <param name='capture'>
/// A Boolean value that specifies the event phase to add the event handler for:
/// true
/// Remove the capturing phase event handler
/// false
/// Remove the bubbling phase event handler
/// </param>
/// <returns>
/// None
/// </returns>
this._host.removeEventListener(eventName, eventCallback, capture);
},
selectColor: function (color) {
this._currentSelection = this._colorDomElements[color];
this._selectColorBlock(this._currentSelection);
this._fireEvent("colorselected", { color: this._colorDomElements[color].style.background, colorId: color });
},
width: {
set: function (newWidth) {
this._colorBlockWidth = newWidth / this._colors.length;
for (var i = 0, len = this._colors.length; i < len; i++) {
var colorBlock = this._colorDomElements[i];
colorBlock.style.width = this._colorBlockWidth + "px";
colorBlock.style.left = i * this._colorBlockWidth + "px";
colorBlock.style.minWidth = colorBlock.style.width;
if (this._selectedColor) {
this._selectColorBlock(this._selectedColor);
}
}
}
},
// Raises an event to listeners
_fireEvent: function (eventName, payload) {
if (document.createEvent) {
var eventObject = document.createEvent("Event");
eventObject.initEvent(eventName, true, false);
if (payload) {
eventObject.payload = payload;
}
this._host.dispatchEvent(eventObject);
}
},
// Expands a specified color block element
_expandColorBlock: function (colorBlockElement, animated, animateNeighbors) {
var currentId = parseInt(colorBlockElement.id);
var newLeft = (currentId * this._colorBlockWidth) - (this._options.hoverWidth - this._colorBlockWidth) / 2;
if (animated) {
WinUtils.addClass(colorBlockElement, "colorpicker-animatedColor");
}
colorBlockElement.style.zIndex = 999;
colorBlockElement.style.width = this._options.hoverWidth + "px";
colorBlockElement.style.height = this._options.hoverHeight + "px";
colorBlockElement.style.bottom = this._options.bottomPosition - (this._options.hoverHeight - this._options.height) / 2 + "px";
colorBlockElement.style.left = newLeft + "px";
WinUtils.addClass(colorBlockElement, "colorpicker-selectedColor");
if (animated) {
WinUtils.removeClass(colorBlockElement, "colorpicker-animatedColor");
}
for (var i = 0, widthCount = 0; i < this._options.affectedNeighbors; i++) {
var newHeight = this._options.hoverHeight - (((this._options.hoverHeight - this._options.height) / (this._options.affectedNeighbors + 1)) * (i + 1));
var newWidth = this._options.hoverWidth - (((this._options.hoverWidth - this._colorBlockWidth) / (this._options.affectedNeighbors + 1)) * (i + 1));
if (this._colorDomElements[currentId + i + 1]) {
this._colorDomElements[currentId + i + 1].zIndex = 1;
this._colorDomElements[currentId + i + 1].style.height = newHeight + "px";
this._colorDomElements[currentId + i + 1].style.bottom = this._options.bottomPosition - (newHeight - this._options.height) / 2 + "px";
if (animateNeighbors && this._colorDomElements[currentId + i + 1] !== this._lastExpansion) {
WinUtils.addClass(this._colorDomElements[currentId + i + 1], "colorpicker-animatedColor");
}
}
if (this._colorDomElements[currentId - i - 1]) {
this._colorDomElements[currentId - i - 1].style.height = newHeight + "px";
this._colorDomElements[currentId - i - 1].style.bottom = this._options.bottomPosition - (newHeight - this._options.height) / 2 + "px";
if (animateNeighbors && this._colorDomElements[currentId - i - 1] !== this._lastExpansion) {
WinUtils.addClass(this._colorDomElements[currentId - i - 1], "colorpicker-animatedColor");
}
}
widthCount += newWidth;
}
},
// Contracts a specified color block element
_contractColorBlock: function (colorBlockElement, animated, animateNeighbors) {
var currentId = parseInt(colorBlockElement.id);
if (animated) {
WinUtils.addClass(colorBlockElement, "colorpicker-animatedColor");
}
colorBlockElement.style.width = this._colorBlockWidth + "px";
colorBlockElement.style.height = this._options.height + "px";
colorBlockElement.style.bottom = this._options.bottomPosition + "px";
colorBlockElement.style.left = (currentId * this._colorBlockWidth) + "px";
WinUtils.removeClass(colorBlockElement, "colorpicker-selectedColor");
if (animated) {
WinUtils.removeClass(colorBlockElement, "colorpicker-animatedColor");
}
for (var i = 0; i < this._options.affectedNeighbors; i++) {
if (this._colorDomElements[currentId + i + 1]) {
if (animateNeighbors) {
WinUtils.addClass(this._colorDomElements[currentId + i + 1], "colorpicker-animatedColor");
}
this._colorDomElements[currentId + i + 1].style.height = this._options.height + "px";
this._colorDomElements[currentId + i + 1].style.bottom = this._options.bottomPosition + "px";
}
if (this._colorDomElements[currentId - i - 1]) {
if (animateNeighbors) {
WinUtils.addClass(this._colorDomElements[currentId - i - 1], "colorpicker-animatedColor");
}
this._colorDomElements[currentId - i - 1].style.height = this._options.height + "px";
this._colorDomElements[currentId - i - 1].style.bottom = this._options.bottomPosition + "px";
}
}
if (!animated) {
colorBlockElement.style.zIndex = 1;
}
this._lastExpansion = colorBlockElement;
},
// Applies selection style to a specified color block
_selectColorBlock: function (colorBlockElement) {
var currentId = parseInt(colorBlockElement.id);
WinUtils.addClass(colorBlockElement, "colorpicker-selectedColor");
colorBlockElement.style.height = this._options.selectedHeight + "px";
colorBlockElement.style.width = this._options.selectedWidth + "px";
colorBlockElement.style.bottom = this._options.bottomPosition - (this._options.selectedHeight - this._options.height) / 2 + "px";
colorBlockElement.style.left = (currentId * this._colorBlockWidth) - (this._options.selectedWidth - this._colorBlockWidth) / 2 + "px";
colorBlockElement.style.zIndex = 999;
},
// Applies selection style to a specified color block
_unselectColorBlock: function (colorBlockElement) {
var currentId = parseInt(colorBlockElement.id);
WinUtils.removeClass(colorBlockElement, "colorpicker-selectedColor");
colorBlockElement.style.height = this._options.height + "px";
colorBlockElement.style.width = this._colorBlockWidth + "px";
colorBlockElement.style.bottom = this._options.bottomPosition + "px";
colorBlockElement.style.left = (currentId * this._colorBlockWidth) + "px";
colorBlockElement.style.zIndex = 1;
}
})
});
})(Microsoft);

View file

@ -0,0 +1,104 @@
////////////////////////////////////////////////////////////
//// © Microsoft. All rights reserved. ////
////////////////////////////////////////////////////////////
(function (AppNS) {
'use strict';
var Canvas = WinJS.Class.define(
// Constructor
function (domCanvas) {
this.domCanvas = domCanvas;
this.context = domCanvas.getContext("2d");
this.depth = 10;
this.history = [];
this.index = 0;
this.minIndex = 0;
this.maxIndex = 0;
this.history[this.index] = this.context.getImageData(0, 0, this.domCanvas.width, this.domCanvas.height);
},
// Variables and Methods
{
// Returns the color of the pixel at the given position
getColorAt: function (color, x, y) {
var imgData = this.context.getImageData(x, y, 1, 1);
color.fromData(imgData.data);
},
// Draws a circle using the current fill style at the given position with
// the given radius.
drawCircle: function (x, y, r) {
this.context.beginPath();
this.context.arc(x, y, r, 0, Math.PI * 2, true);
this.context.closePath();
this.context.fill();
},
// Saves the current state of the canvas, deleting all previously undone (future or forward) state.
saveState: function () {
this.index++;
this.maxIndex = this.index;
if (this.maxIndex - this.minIndex > this.depth) {
this.history[this.minIndex] = null;
this.minIndex = this.maxIndex - this.depth;
}
this.history[this.index] = this.context.getImageData(0, 0, this.domCanvas.width, this.domCanvas.height);
},
// Loads the state stored at the given index into the canvas
loadState: function (i) {
if (i >= this.minIndex && i <= this.maxIndex) {
this.index = i;
this.context.putImageData(this.history[this.index], 0, 0);
} else {
throw new Error("PaintPlay - Error: Index out of bounds");
}
},
// Returns true if able to undo (still some past actions)
canUndo: function () {
return this.index > this.minIndex;
},
// Returns true if able to redo (still some future actions)
canRedo: function () {
return this.index < this.maxIndex;
},
// Undoes the last action (goes back one step in state history)
undo: function () {
if (this.canUndo()) {
this.loadState(this.index - 1);
}
},
// Redoes the next action (goes forward one step in state history)
redo: function () {
if (this.canRedo()) {
this.loadState(this.index + 1);
}
},
// Clears the canvas.
clear: function () {
this.context.clearRect(0, 0, this.domCanvas.width, this.domCanvas.height);
this.saveState();
},
// Returns an image object containing the current state of the canvas.
getImage: function () {
var img = new Image();
var imgSrc = this.domCanvas.toDataURL("image/png");
img.src = imgSrc;
return img;
},
}
);
WinJS.Namespace.defineWithParent(AppNS, "Painter.Graphics", {
Canvas: Canvas
});
})(Microsoft.Paint);

View file

@ -0,0 +1,146 @@
////////////////////////////////////////////////////////////
//// © Microsoft. All rights reserved. ////
////////////////////////////////////////////////////////////
(function (AppNS) {
'use strict';
// The Color class represents a color in RGBA format.
var Color = WinJS.Class.define(
// Constructor
function (r, g, b, a) {
this.red = this.clip(r) || 0; // Red value (0-255)
this.green = this.clip(g) || 0; // Green value (0-255)
this.blue = this.clip(b) || 0; // Blue value (0-255)
this.alpha = this.clipAlpha(a) || 0; // Alpha value (0.0-1.0)
this.blendAmount = 0.8; // The speed of the Blending algorithm
},
// Variables and Functions
{
// Clips the value to between 255 and 0
clip: function (value) {
value = Math.round(value);
if (value > 255) {
return 255;
} else if (value < 0) {
return 0;
} else {
return value;
}
},
// Clips the alpha value to between 0 and 1.0
clipAlpha: function (alpha) {
if (alpha > 1.0) {
return this.clip(alpha) / 255;
} else if (alpha < 0.0 || isNaN(alpha)) {
return 0.0;
} else {
return alpha;
}
},
// Scales this color by a constant
scale: function (constant) {
this.red = this.clip(Math.round(this.red * constant));
this.green = this.clip(Math.round(this.green * constant));
this.blue = this.clip(Math.round(this.blue * constant));
},
// Sets this color to the given rgba values
fromRGBA: function (r, g, b, a) {
this.red = this.clip(r);
this.green = this.clip(g);
this.blue = this.clip(b);
this.alpha = this.clipAlpha(a);
},
// Sets this color to the pixel array info
fromData: function (data) {
this.fromRGBA(data[0], data[1], data[2], data[3]);
},
// Sets this color to the given color
fromColor: function (color) {
this.fromRGBA(color.red, color.green, color.blue, color.alpha);
},
/// <summary>Sets this color to be the color described by the formatted string.</summary>
/// <param name="str" type="String">
/// Str is a formatted string representation of a color. It must be one of the following formats:
/// rgba: "rgba({r}, {g}, {b}, {a})"
/// rgb (alpha auto set to 1.0): "rgb({r}, {g}, {b})"
/// Hex (alpha auto set to 1.0): "#{rr}{gg}{bb}"
/// </param>
fromString: function (str) {
if (str.indexOf("rgba") === 0) {
var strData = str.slice(5, -1);
var data = strData.split(",");
this.red = this.clip(parseInt(data[0]));
this.green = this.clip(parseInt(data[1]));
this.blue = this.clip(parseInt(data[2]));
this.alpha = this.clipAlpha(parseFloat(data[3]));
} else if (str.indexOf("rgb") === 0) {
var strData = str.slice(4, -1);
var data = strData.split(",");
this.red = this.clip(parseInt(data[0]));
this.green = this.clip(parseInt(data[1]));
this.blue = this.clip(parseInt(data[2]));
this.alpha = 1.0;
} else if (str.indexOf("#") === 0) {
this.red = this.clip(parseInt(str.slice(1, 3), 16));
this.green = this.clip(parseInt(str.slice(3, 5), 16));
this.blue = this.clip(parseInt(str.slice(5, 7), 16));
this.alpha = 1.0;
}
},
// Returns a new copy of this color
copy: function () {
var r = this.red;
var g = this.green;
var b = this.blue;
var a = this.alpha;
return new AppNS.Painter.Graphics.Color(r, g, b, a);
},
// Sets this color to be a weighted average of this color and the given color.
// A good weight is 80% this color, 20% new color.
blendWith: function (color) {
if (color.alpha > 0.01) { // Make sure the color we're blending with exists, don't want to blend with invisible ink!
this.red = Math.floor(this.red * this.blendAmount + color.red * (1 - this.blendAmount));
this.green = Math.floor(this.green * this.blendAmount + color.green * (1 - this.blendAmount));
this.blue = Math.floor(this.blue * this.blendAmount + color.blue * (1 - this.blendAmount));
this.alpha = this.alpha * this.blendAmount + color.alpha * (1 - this.blendAmount);
}
},
// Lightens this color by the given amount
lighten: function (amount) {
this.red += this.clip((255 - this.red) * amount);
this.green += this.clip((255 - this.green) * amount);
this.blue += this.clip((255 - this.blue) * amount);
},
// Darkens this color by the given amount
darken: function (amount) {
this.red -= this.clip(this.red * amount);
this.green -= this.clip(this.green * amount);
this.blue -= this.clip(this.blue * amount);
},
// Returns the Color in a String representation of the form "rgba(<r>, <g>, <b>, <a>)"
toString: function () {
return "rgba(" + this.red + "," + this.green + "," + this.blue + "," + this.alpha + ")";
}
}
);
WinJS.Namespace.defineWithParent(AppNS, "Painter.Graphics", {
Color: Color
});
})(Microsoft.Paint);

View file

@ -0,0 +1,294 @@
////////////////////////////////////////////////////////////
//// © Microsoft. All rights reserved. ////
////////////////////////////////////////////////////////////
(function (AppNS) {
'use strict';
var Path = WinJS.Class.define(
// Constructor
function (data) {
// Params
this.minDist = 3; // Minumum distance before a point is added to the path
this.weight = 0.3; // Bezier weight of control points
this.p1 = null; // The first point
this.p2 = null; // The second point
this.p3 = null; // The third point (most recent)
this.box1 = null; // Touch bounding box data for the first point
this.box2 = null; // Touch bounding box data for the second point
this.box3 = null; // Touch bounding box data for the third point
this.dist = 0; // Current distance between the start and end points of the bezier curve
this.wStart = 0; // The width of the line at the beginning of the curve
this.wEnd = 0; // The width of the line at the end of the curve
this.dirStart = new AppNS.Painter.Math.Vector(0, 0); // Direction at beginning of curve
this.dirEnd = new AppNS.Painter.Math.Vector(0, 0); // Direction at the end of curve
this.perpStart = new AppNS.Painter.Math.Vector(0, 0); // Perpendicular at beginning of curve
this.perpEnd = new AppNS.Painter.Math.Vector(0, 0); // Perpendicular at end of curve
this.pStart = new AppNS.Painter.Math.Point(0, 0); // Point at beginning of curve
this.cpStart = new AppNS.Painter.Math.Point(0, 0); // Bezier control point at beginning of curve
this.cpEnd = new AppNS.Painter.Math.Point(0, 0); // Bezier control point at end of curve
this.pEnd = new AppNS.Painter.Math.Point(0, 0); // Point at end of curve
this.newPoint = false; // True if the there is a new point added to the path
if (data.x || data.y) {
this.p3 = new AppNS.Painter.Math.Point(data.x, data.y); // The third point (most recent)
} else {
throw new Error("PaintPlay - Error: Path object requires x and y data info.");
}
if (data.w && data.h) {
this.box3 = new AppNS.Painter.Math.Vector(data.w, data.h);
}
},
// Variables and Methods
{
// True if there is information necessary to draw the start of a curve (2 points)
starting: {
get: function () {
return (this.newPoint && this.numPoints() === 2);
}
},
// True if this path is new (1 point)
created: {
get: function () {
return (this.numPoints() === 1);
}
},
// True if this path is drawable (3 points)
drawable: {
get: function () {
return (this.newPoint && this.numPoints() === 3);
}
},
numPoints: function () {
if (this.p1) {
return 3;
} else if (this.p2) {
return 2;
} else if (this.p3) {
return 1;
} else {
return 0;
}
},
// Adds the given point to the path
addPoint: function (data) {
// Add the new point if mouse moved far enough
if (data.x || data.y) {
var p = new AppNS.Painter.Math.Point(data.x, data.y);
} else {
throw new Error("PaintPlay - Error: Path object requires x and y data info.");
}
if (data.w && data.h) {
var box = new AppNS.Painter.Math.Vector(data.w, data.h);
}
if (this.p3.distanceTo(p) > this.minDist) {
this.newPoint = true;
// Reset the points of the curve
this.p1 = this.p2;
this.p2 = this.p3;
this.p3 = p;
this.box1 = this.box2;
this.box2 = this.box3;
this.box3 = box;
// If only have two points, initialize first direction vector
if (!this.p1) {
// Find the current distance
this.dist = this.p2.distanceTo(this.p3);
// Initialize tangent info
this.dirEnd.fromPoints(this.p2, this.p3);
this.dirEnd.normalize();
this.perpEnd.fromVector(this.dirEnd);
this.perpEnd.toPerp();
this.perpEnd.normalize();
} else {
// otherwise, we have all three points
// Find the current distance
this.dist = this.p1.distanceTo(this.p2);
// Set start and end tangent info
this.dirStart.fromVector(this.dirEnd);
this.dirEnd.fromPoints(this.p1, this.p3);
this.dirEnd.normalize();
// Set perpendicular vectors from start and end tangents
this.perpStart.fromVector(this.perpEnd);
this.perpEnd.fromVector(this.dirEnd);
this.perpEnd.toPerp();
this.perpEnd.normalize();
}
} else {
this.newPoint = false;
}
},
// Calculates the end Bezier curve info
calculateEndBezier: function (offsetStart, offsetEnd, inertia) {
if (this.p2 && this.p3) {
// If offset given, use it
var c2 = offsetStart || 0;
var c3 = offsetEnd || 0;
inertia = inertia || 0;
var dir3 = new AppNS.Painter.Math.Vector();
dir3.fromPoints(this.p2, this.p3);
dir3.normalize();
var perp3 = new AppNS.Painter.Math.Vector();
perp3.fromVector(dir3);
perp3.toPerp();
perp3.normalize();
// First bezier point
this.pStart.fromPoint(this.p2);
this.pStart.scaleAdd(this.perpEnd, c2);
// Last bezier point
this.pEnd.fromPoint(this.p3);
this.pEnd.scaleAdd(perp3, c3);
this.pEnd.scaleAdd(dir3, inertia);
var dist = this.pStart.distanceTo(this.pEnd);
// First control point
this.cpStart.fromPoint(this.pStart);
this.cpStart.scaleAdd(this.dirEnd, dist * this.weight);
// Second control point
this.cpEnd.fromPoint(this.pEnd);
this.cpEnd.scaleAdd(dir3, -1 * dist * this.weight);
}
},
calculateInertia: function (inertia, dist, min, max) {
var cInertia = dist * inertia;
if (cInertia < min) {
cInertia = min;
} else if (cInertia > max) {
cInertia = max;
}
return cInertia;
},
calculateStartCurve: function (offsetTop, offsetBottom, inertia, minInertia, overlap) {
if (this.p2 && this.p3) {
// If offset given, use it
var cTop = offsetTop || 0;
var cBottom = offsetBottom || 0;
var cOverlap = overlap || 0;
var cInertia = -this.calculateInertia(inertia, this.dist, minInertia, 4 * minInertia);
// First bezier point
this.pStart.fromPoint(this.p2);
this.pStart.scaleAdd(this.dirEnd, cOverlap);
this.pStart.scaleAdd(this.perpEnd, -1 * cBottom);
// Last bezier point
this.pEnd.fromPoint(this.p2);
this.pEnd.scaleAdd(this.dirEnd, cOverlap);
this.pEnd.scaleAdd(this.perpEnd, cTop);
// First control point
this.cpStart.fromPoint(this.pStart);
this.cpStart.scaleAdd(this.dirEnd, cInertia);
// Second control point
this.cpEnd.fromPoint(this.pEnd);
this.cpEnd.scaleAdd(this.dirEnd, cInertia);
}
},
calculateStopCurve: function (offsetTop, offsetBottom, inertia, minInertia, overlap) {
if (this.p2 && this.p3) {
// If offset given, use it
var cTop = offsetTop || 0;
var cBottom = offsetBottom || 0;
var cOverlap = overlap || 0;
var cInertia = this.calculateInertia(inertia, this.dist, minInertia, 4 * minInertia);
// First bezier point
this.pStart.fromPoint(this.p2);
this.pStart.scaleAdd(this.dirEnd, -cOverlap);
this.pStart.scaleAdd(this.perpEnd, -1 * cBottom);
// Last bezier point
this.pEnd.fromPoint(this.p2);
this.pEnd.scaleAdd(this.dirEnd, -cOverlap);
this.pEnd.scaleAdd(this.perpEnd, cTop);
// First control point
this.cpStart.fromPoint(this.pStart);
this.cpStart.scaleAdd(this.dirEnd, cInertia);
// Second control point
this.cpEnd.fromPoint(this.pEnd);
this.cpEnd.scaleAdd(this.dirEnd, cInertia);
}
},
// Calculates the Bezier curve info.
// Offsets are the number of pixels perpendicular to main points (shifts curve up or down).
calculateBezier: function (offsetStart, offsetEnd, overlap) {
// If offset given, use it
var c1 = offsetStart || 0;
var c2 = offsetEnd || 0;
var c3 = overlap || 0;
// First bezier point
this.pStart.fromPoint(this.p1);
this.pStart.scaleAdd(this.dirStart, -c3);
this.pStart.scaleAdd(this.perpStart, c1);
// Last bezier point
this.pEnd.fromPoint(this.p2);
this.pEnd.scaleAdd(this.dirEnd, c3);
this.pEnd.scaleAdd(this.perpEnd, c2);
var dist = this.pStart.distanceTo(this.pEnd);
// First control point
this.cpStart.fromPoint(this.pStart);
this.cpStart.scaleAdd(this.dirStart, dist * this.weight);
// Second control point
this.cpEnd.fromPoint(this.pEnd);
this.cpEnd.scaleAdd(this.dirEnd, -1 * dist * this.weight);
},
// Calculates the line width based on touch-box data
calculateWidth: function () {
if (this.box1 && this.box2) {
this.wStart = this.box1.componentIn(this.perpStart);
this.wEnd = this.box2.componentIn(this.perpEnd);
} else {
this.wStart = 10;
this.wEnd = 30;
}
}
});
WinJS.Namespace.defineWithParent(AppNS, "Painter.Graphics", {
Path: Path
});
})(Microsoft.Paint);

View file

@ -0,0 +1,87 @@
////////////////////////////////////////////////////////////
//// © Microsoft. All rights reserved. ////
////////////////////////////////////////////////////////////
(function (AppNS) {
'use strict';
// A Texture has an image that is processed and given to a canvas object
var Texture = WinJS.Class.define(
// Constructor
function (image) {
onload = onload || function () {};
// The canvas containing the colored texture
this.size = 40;
this.colorCanvas = document.createElement("canvas");
this.colorCanvas.height = this.size;
this.colorCanvas.width = this.size;
this.ctx = this.colorCanvas.getContext("2d");
// The ImageData object used to draw pixels on colored texture
this.copyCanvas = document.createElement("canvas");
this.copyCanvas.height = this.size;
this.copyCanvas.width = this.size;
this.copyCtx = this.copyCanvas.getContext("2d");
this.copy = this.copyCtx.createImageData(this.size, this.size);
// The canvas containing the scaled texture
this.textureCanvas = document.createElement("canvas");
this.textureCtx = this.textureCanvas.getContext("2d");
this.textureColor = new AppNS.Painter.Graphics.Color();
this.textureCanvas.width = this.size;
this.textureCanvas.height = this.size;
this.texturePattern = null;
// The original image of the texture
this.ctx.drawImage(image, 0, 0);
this.imageData = this.ctx.getImageData(0, 0, this.size, this.size);
},
// Variables and Methods
{
/// <summary>
/// Updates the canvas with the given zoom and color
/// </summary>
/// <param type="Microsoft.Paint.Painter.Graphics.Color" name="color">
/// The color of the pixels in the texture.
/// </param>
/// <param type="number" name="zoom">
/// How much the texture is zoomed in. Zoom = 1 means normal size,
/// Zoom = 2 means zoomed in 2x, etc.
/// </param>
update: function (color, zoom) {
this.textureColor.fromColor(color);
this.textureCanvas.width = zoom * this.size;
this.textureCanvas.height = zoom * this.size;
this.replacePixels(this.textureColor);
this.texturePattern = this.ctx.createPattern(this.textureCanvas, "repeat");
},
// Replaces all pixels with pixels of the given color.
// Opacity is taken from the texture though.
replacePixels: function (color) {
for (var x = 0; x < this.size; x++) {
for (var y = 0; y < this.size; y++) {
var k = (y * this.size + x) * 4;
var alpha = this.imageData.data[k + 3];
this.copy.data[k + 0] = color.red;
this.copy.data[k + 1] = color.green;
this.copy.data[k + 2] = color.blue;
this.copy.data[k + 3] = alpha;
}
}
this.ctx.putImageData(this.copy, 0, 0);
this.textureCtx.globalCompositeOperation = "copy";
this.textureCtx.drawImage(this.colorCanvas, 0, 0, this.size, this.size, 0, 0, this.textureCanvas.width, this.textureCanvas.height);
},
}
);
WinJS.Namespace.defineWithParent(AppNS, "Painter.Graphics", {
Texture: Texture
});
})(Microsoft.Paint);

View file

@ -0,0 +1,77 @@
////////////////////////////////////////////////////////////
//// © Microsoft. All rights reserved. ////
////////////////////////////////////////////////////////////
(function (AppNS) {
'use strict';
// The Point class represents a point in 2-Dimensional space using Cartesian Coordinates.
var Point = WinJS.Class.define(
// Constructor
function (x, y) {
// The position of the point in 2d space, cartesian coords
this.x = x || 0;
this.y = y || 0;
},
// Variables and Functions
{
// Returns the distance between this and another point
distanceTo: function (point) {
var dx = point.x - this.x;
var dy = point.y - this.y;
return Math.sqrt(dx * dx + dy * dy);
},
// Creates a Vector pointing from this point to point p
createVectorTo: function (point) {
return new AppNS.Painter.Math.Vector(point.x - this.x, point.y - this.y);
},
// Sets this point to the result of adding the Vector v to this point
add: function (vector) {
this.x = this.x + vector.x;
this.y = this.y + vector.y;
},
// Sets this point to this point translated by the scaled vector
scaleAdd: function (vector, constant) {
this.x = this.x + (vector.x * constant);
this.y = this.y + (vector.y * constant);
},
// Sets this point to the result of subtracting the Vector v from this point
sub: function (vector) {
this.x = this.x - vector.x;
this.y = this.y - vector.y;
},
// Returns true if this point is the same as p
equals: function (point) {
return this.x === point.x && this.y === point.y;
},
// Sets this point to the given x and y coords
fromXY: function (x, y) {
this.x = x;
this.y = y;
},
// Sets this point to the given point
fromPoint: function (point) {
this.x = point.x;
this.y = point.y;
},
// Returns a copy of this point
copy: function () {
return new AppNS.Painter.Math.Point(this.x, this.y);
}
});
WinJS.Namespace.defineWithParent(AppNS, "Painter.Math", {
Point: Point
});
})(Microsoft.Paint);

View file

@ -0,0 +1,143 @@
////////////////////////////////////////////////////////////
//// © Microsoft. All rights reserved. ////
////////////////////////////////////////////////////////////
(function (AppNS) {
'use strict';
// The Vector class represents a position-less vector in 2-Dimensional space.
// A Vector has direction and length, both represented in the form of change in x and change in y.
var Vector = WinJS.Class.define(
// Constructor
function (x, y) {
this.x = x || 0; // The x-component of this vector
this.y = y || 0; // The y-component of this vector
},
// Variables and Functions
{
// The length of this vector, as calculated by it's x and y components
length: {
get: function () {
return Math.sqrt(this.x * this.x + this.y * this.y);
},
set: function (length) {
this.normalize();
this.scale(length);
}
},
// Return the dot product of this vector and v
dot: function (vector) {
return this.x * vector.x + this.y * vector.y;
},
// Sets this vector to the given x and y
fromXY: function (x, y) {
this.x = x;
this.y = y;
},
// Set this vector to the given vector
fromVector: function (vector) {
this.x = vector.x;
this.y = vector.y;
},
// Create a vector from point1 pointing towards point2
fromPoints: function (point1, point2) {
this.x = point2.x - point1.x;
this.y = point2.y - point1.y;
},
// Rotates this vector by 90 degrees counter-clockwise, making it perpendicular to what it was
toPerp: function () {
var temp = this.x;
this.x = -1 * this.y
this.y = temp;
},
// Rotates this vector counter-clockwise by angle in radians
rotate: function (angle) {
var cos = Math.cos(angle);
var sin = Math.sin(angle);
var x = this.x;
var y = this.y;
this.x = x * cos - y * sin;
this.y = x * sin + y * cos;
},
// Scales this vector by a constant
scale: function (constant) {
this.x = this.x * constant;
this.y = this.y * constant;
},
// Returns the angle in radians between this vector and v
angle: function (vector) {
var l1 = this.length;
var l2 = vector.length;
if (l1 !== 0 && l2 !== 0) {
var val = this.dot(vector) / (l1 * l2);
val = val > 1.0 ? 1.0 : val;
val = val < -1.0 ? -1.0 : val;
return Math.acos(val);
}
},
// Returns the slope (dy/dx) of this vector
getSlope: function () {
return this.y / this.x;
},
// Normalizes (sets to length 1) this vector
normalize: function () {
var length = this.length;
if (length !== 0) {
this.scale(1 / length);
}
},
// Sets this vector to the result of subtracting the given vector from this
sub: function (vector) {
this.x = this.x - vector.x;
this.y = this.y - vector.y;
},
// Sets this vector to the result of adding the given vector to this
add: function (vector) {
this.x = this.x + vector.x;
this.y = this.y + vector.y;
},
// Returns true if this vector is equal to another
equals: function (vector) {
return this.x === vector.x && this.y === vector.y;
},
// Returns a copy of this vector
copy: function () {
return new AppNS.Painter.Math.Vector(this.x, this.y);
},
// Projects this vector onto the given direction.
// Note: the length of the direction vector is irrelevant.
project: function (vector) {
var direction = vector.copy();
var length = this.componentIn(direction);
direction.length = length;
this.fromVector(direction);
},
// Returns the length of the component of this vector in the given vector's direction
componentIn: function (vector) {
return this.dot(vector) / vector.length;
}
});
WinJS.Namespace.defineWithParent(AppNS, "Painter.Math", {
Vector: Vector
});
})(Microsoft.Paint);

151
Controls/Paint/Painter.js Normal file
View file

@ -0,0 +1,151 @@
////////////////////////////////////////////////////////////
//// © Microsoft. All rights reserved. ////
////////////////////////////////////////////////////////////
(function (AppNS) {
'use strict';
var Painter = WinJS.Class.define(
// Constructor
function (domCanvas) {
this.paths = []; // The set of paths being built from
this.pathCounted = []; // Array of boolean flags to tell if a path has been counted or not
this.pathCount = 0; // Current count of active paths (number of fingers on the canvas)
this.toolName = "marker"; // The currently selected tool's name
this.tools = {}; // All of the tools used by this Painter
this.canvas = new AppNS.Painter.Graphics.Canvas(domCanvas); // The current canvas this Painter is attached to
this._currentColor = new AppNS.Painter.Graphics.Color(0,0,0,1); // The current color selected
this.loadTool(new AppNS.Painter.Tools.Marker(), "marker"); // Every Painter has the Marker by default
},
// Variables and Functions
{
// The currently selected tool
tool: {
get: function () {
if (this.tools.hasOwnProperty(this.toolName)) {
return this.tools[this.toolName];
} else {
return null;
}
},
set: function (newTool) {
if (this.tools.hasOwnProperty(newTool)) {
this.toolName = newTool;
} else {
throw new Error("PaintPlay - Error: No such tool.");
}
}
},
color: {
get: function () {
return this._currentColor;
},
set: function (newColor) {
this._currentColor.fromColor(newColor);
this.updateTools();
}
},
updateTools: function () {
for (var tool in this.tools){
if (this.tools[tool].update) {
this.tools[tool].update(this);
}
}
},
// Adds the given tool with the given string name to the Painter.
// Default Painter has only a marker (simple line drawer).
loadTool: function (tool, name) {
this.tools[name] = tool;
this.updateTools();
},
/// <summary>
/// Begins drawing the path with the current tool.
/// </summary>
/// <param name="data" type="data">
/// The data parameter is an object with the following properties:
/// - x (required, number): the x position on the canvas
/// - y (required, number): the y position on the canvas
/// - w (optional, number from [0, inf)): the width of the touch input box
/// - h (optional, number from [0, inf)): the height of the touch input box
/// - id (optional, number): the ID of this path, usually taken from the touch input's pointerId
/// </param>
drawStart: function (data) {
if (this.canvas) {
var id = data.id || 0;
this.paths[id] = new AppNS.Painter.Graphics.Path(data);
this.pathCounted[id] = false;
if (this.tool.renderStart) {
this.tool.renderStart(this.paths[id], this);
}
}
},
/// <summary>
/// Begins drawing the path with the current tool.
/// </summary>
/// <param name="data" type="data">
/// The data parameter is an object with the following properties:
/// - x (required, number): the x position on the canvas
/// - y (required, number): the y position on the canvas
/// - w (optional, number from [0, inf)): the width of the touch input box
/// - h (optional, number from [0, inf)): the height of the touch input box
/// - id (optional, number): the ID of this path, usually taken from the touch input's pointerId
/// </param>
draw: function (data) {
var id = data.id || 0;
var path = this.paths[id];
if (!this.pathCounted[id]) {
this.pathCount++;
this.pathCounted[id] = true;
}
if (path && this.canvas) {
path.addPoint(data);
this.tool.render(path, this);
}
},
/// <summary>
/// Begins drawing the path with the current tool.
/// </summary>
/// <param name="data" type="data">
/// The data parameter is an object with the following properties:
/// - x (required, number): the x position on the canvas
/// - y (required, number): the y position on the canvas
/// - w (optional, number from [0, inf)): the width of the touch input box
/// - h (optional, number from [0, inf)): the height of the touch input box
/// - id (optional, number): the ID of this path, usually taken from the touch input's pointerId
/// </param>
drawStop: function (data) {
var id = data.id || 0;
var path = this.paths[id];
if (path && this.canvas) {
path.addPoint(data);
if (this.pathCounted[id]) {
this.pathCount--;
this.pathCounted[id] = false;
}
this.tool.render(path, this);
if (this.tool.renderStop) {
this.tool.renderStop(path, this);
}
if (this.pathCount === 0) {
this.canvas.saveState();
}
}
}
}
);
WinJS.Namespace.defineWithParent(AppNS, "Painter", {
Painter: Painter
});
})(Microsoft.Paint);

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View file

@ -0,0 +1,25 @@
////////////////////////////////////////////////////////////
//// © Microsoft. All rights reserved. ////
////////////////////////////////////////////////////////////
(function (AppNS) {
'use strict';
// A Bristle simulates the bristle on a brush.
var Bristle = WinJS.Class.define(
// Constructor
function () {
this.color = new AppNS.Painter.Graphics.Color(); // The color of the bristle
this.width = 2; // The bristle width is 2px
},
// Variables and Methods
{}
);
WinJS.Namespace.defineWithParent(AppNS, "Painter.Tools", {
Bristle: Bristle
});
})(Microsoft.Paint);

View file

@ -0,0 +1,176 @@
////////////////////////////////////////////////////////////
//// © Microsoft. All rights reserved. ////
////////////////////////////////////////////////////////////
(function (AppNS) {
'use strict';
// The Brush tool simulates a PaintBrush, with color blending and paint loss over distance.
var Brush = WinJS.Class.define(
// Constructor
function () {
this.blends = false; // Sets whether or not this brush blends with underlying color
this.fade = 1000; // How quickly the brush fades, in pixels (0 = no fade)
this.numBristles = 0; // Number of bristles on each side of the brush.
this.bristles = []; // All of the Brush Bristles
this.pxPerBristle = 3; // The width of each bristle
this.maxA = 1.0; // Maximum beginning opacity
this.minA = 0.8; // Minimum beginning opacity
this.autoWidth = false; // Whether or not width is based on path touch-box data or manual width value
this._width = 0;
this.width = 20;
},
// Variables and Methods
{
// The width of the brush, to be approximated using bristles
width: {
get: function () {
return this._width;
},
set: function (w) {
this.numBristles = Math.floor(0.3 * w / this.pxPerBristle);
if (this.numBristles <= 0) {
this.numBristles = 0;
}
this._width = (2 * this.numBristles + 1) * this.pxPerBristle;
}
},
// Begins rendering the beginning of the path
renderStart: function (path, painter) {
var ctx = painter.canvas.context;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.shadowBlur = 0;
ctx.globalCompositeOperation = "source-over";
ctx.globalAlpha = 1.0;
var startColor = painter.color.copy();
startColor.alpha = this.maxA;
ctx.fillStyle = startColor.toString();
for (var i = -this.numBristles; i <= this.numBristles; i++) {
this.bristles[i] = new AppNS.Painter.Tools.Bristle();
this.bristles[i].color.fromColor(startColor);
this.bristles[i].width = this.pxPerBristle + 0.5;
}
},
// Renders the given path using the current Marker style
render: function (path, painter) {
var ctx = painter.canvas.context;
// If just starting the path, draw semicircle
if (path.starting) {
path.calculateStartCurve(this.width / 2, this.width / 2, 3, this.width);
ctx.beginPath();
ctx.moveTo(path.pStart.x, path.pStart.y);
ctx.bezierCurveTo(path.cpStart.x, path.cpStart.y, path.cpEnd.x, path.cpEnd.y, path.pEnd.x, path.pEnd.y);
ctx.closePath();
ctx.fill();
}
if (path.drawable) {
// Colors
var blendColor = new AppNS.Painter.Graphics.Color();
var oldColor = new AppNS.Painter.Graphics.Color();
// Draw each simulated bristle as a bezier curve
for (var i = -this.numBristles; i <= this.numBristles; i++) {
// The color at the beginning of the stroke segment
oldColor.fromColor(this.bristles[i].color);
var offsetStart = i * this.pxPerBristle;
var offsetEnd = i * this.pxPerBristle;
path.calculateBezier(offsetStart, offsetEnd, Math.pow(oldColor.alpha, 2) * 0.25);
// Blend bristle's color with canvas color
if (this.blends) {
painter.canvas.getColorAt(blendColor, path.cpStart.x, path.cpStart.y);
this.bristles[i].color.blendWith(blendColor);
}
// Update opacity
var currA = this.bristles[i].color.alpha;
var perturb = 0;
if (this.fade) { // Cause the brush to lose ink over distance
perturb = Math.random() * -(Math.pow(0.2 * i / (this.numBristles + 1), 4) + path.dist / this.fade);
currA = currA + perturb;
} else { // Cause the brush to randomly gain or lose ink per bristle, within minA and maxA
currA = Math.random() * (this.maxA - this.minA) + this.minA;
}
this.bristles[i].color.alpha = this.bristles[i].color.clipAlpha(currA);
this.bristles[i].width = this.pxPerBristle + Math.pow(oldColor.alpha, 2) * 0.5;
// Create a linear gradient from the start of the stoke segment to the end,
// giving a smooth transition of color.
var gradient = ctx.createLinearGradient(path.pStart.x, path.pStart.y, path.pEnd.x, path.pEnd.y);
gradient.addColorStop(0, oldColor.toString());
gradient.addColorStop(1, this.bristles[i].color.toString());
ctx.beginPath();
ctx.moveTo(path.pStart.x, path.pStart.y);
ctx.bezierCurveTo(path.cpStart.x, path.cpStart.y, path.cpEnd.x, path.cpEnd.y, path.pEnd.x, path.pEnd.y);
ctx.lineWidth = this.bristles[i].width;
ctx.strokeStyle = gradient;
ctx.stroke();
}
}
},
// Stops the path with the current style
renderStop: function (path, painter) {
var ctx = painter.canvas.context;
if (path.created || path.starting) {
painter.canvas.drawCircle(path.p3.x, path.p3.y, this.width / 2);
} else {
// Colors
var blendColor = new AppNS.Painter.Graphics.Color();
var oldColor = new AppNS.Painter.Graphics.Color();
// Draw each simulated bristle as a bezier curve
for (var i = -this.numBristles; i <= this.numBristles; i++) {
var offsetStart = i * this.pxPerBristle;
var offsetEnd = i * this.pxPerBristle;
path.calculateEndBezier(offsetStart, offsetEnd, 20);
// The color at the beginning of the stroke segment
oldColor.fromColor(this.bristles[i].color);
// Blend bristle's color with canvas color
if (this.blends) {
painter.canvas.getColorAt(blendColor, path.cpStart.x, path.cpStart.y);
this.bristles[i].color.blendWith(blendColor);
}
// Update opacity
this.bristles[i].color.alpha = 0;
// Create a linear gradient from the start of the stoke segment to the end,
// giving a smooth transition of color.
var gradient = ctx.createLinearGradient(path.pStart.x, path.pStart.y, path.pEnd.x, path.pEnd.y);
gradient.addColorStop(0, oldColor.toString());
gradient.addColorStop(1, this.bristles[i].color.toString());
ctx.beginPath();
ctx.moveTo(path.pStart.x, path.pStart.y);
ctx.bezierCurveTo(path.cpStart.x, path.cpStart.y, path.cpEnd.x, path.cpEnd.y, path.pEnd.x, path.pEnd.y);
ctx.lineWidth = this.bristles[i].width;
ctx.strokeStyle = gradient;
ctx.stroke();
}
}
}
}
);
WinJS.Namespace.defineWithParent(AppNS, "Painter.Tools", {
Brush: Brush
});
})(Microsoft.Paint);

View file

@ -0,0 +1,146 @@
////////////////////////////////////////////////////////////
//// © Microsoft. All rights reserved. ////
////////////////////////////////////////////////////////////
(function (AppNS) {
'use strict';
// The crayon tool uses textures to generate a crayon/pencil/chalk like effect
var Crayon = WinJS.Class.define(
// Constructor
function () {
this._density = 0.5; // The default density is 50% color
this._color = new AppNS.Painter.Graphics.Color(0,0,0,255);
this.autoWidth = false; // Whether or not width is based on path touch-box data or manual width value
this.width = 30; // The default line manual width
this.texture = null;
},
// Variables and Methods
{
density: {
get: function () {
return this._density;
},
set: function (d) {
this._density = d;
if (this.texture) {
this.texture.update(this._color, this._density);
}
}
},
// Starts rendering the given path using the current style
renderStart: function (path, painter) {
var ctx = painter.canvas.context;
ctx.fillStyle = this.texture.texturePattern;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.shadowBlur = 0;
ctx.globalCompositeOperation = "source-over";
ctx.globalAlpha = 1.0;
},
// Renders the given path using the current style
render: function (path, painter) {
var ctx = painter.canvas.context;
// Get start and end widths for the stroke part
var wStart = this.width;
var wEnd = this.width;
if (this.autoWidth){
path.calculateWidth();
wStart = path.wStart;
wEnd = path.wEnd;
}
// If just starting the path, draw semicircle
if (path.starting) {
path.calculateStartCurve(wEnd / 2, wEnd / 2, 3, wEnd);
ctx.beginPath();
ctx.moveTo(path.pStart.x, path.pStart.y);
ctx.bezierCurveTo(path.cpStart.x, path.cpStart.y, path.cpEnd.x, path.cpEnd.y, path.pEnd.x, path.pEnd.y);
ctx.closePath();
this.fillCrayon(ctx);
}
if (path.drawable) {
// Start at first point of shape, then bezier to second
ctx.beginPath();
path.calculateBezier(wStart / 2, wEnd / 2);
ctx.moveTo(path.pStart.x, path.pStart.y);
ctx.bezierCurveTo(path.cpStart.x, path.cpStart.y, path.cpEnd.x, path.cpEnd.y, path.pEnd.x, path.pEnd.y);
// Line to third point of shape, then bezier to fourth, then close the path
path.calculateBezier(-1 * wStart / 2, -1 * wEnd / 2);
ctx.lineTo(path.pEnd.x, path.pEnd.y);
ctx.bezierCurveTo(path.cpEnd.x, path.cpEnd.y, path.cpStart.x, path.cpStart.y, path.pStart.x, path.pStart.y);
ctx.closePath();
// Set fill Style and fill shape
this.fillCrayon(ctx);
}
},
// Renders the end of the path with the current style
renderStop: function (path, painter) {
var ctx = painter.canvas.context;
// Get start and end widths for the stroke part
var wStart = this.width;
var wEnd = this.width;
if (this.autoWidth){
path.calculateWidth();
wStart = path.wStart;
wEnd = path.wEnd;
}
// If haven't drawn starting effect yet
if (path.created || path.starting) {
ctx.beginPath();
ctx.arc(path.p3.x, path.p3.y, wEnd / 2, 0, Math.PI * 2, true);
ctx.closePath();
this.fillCrayon(ctx);
} else { // Otherwise, draw a curve to end the path
path.calculateStopCurve(wEnd / 2, wEnd / 2, 3, wEnd);
ctx.beginPath();
ctx.moveTo(path.pStart.x, path.pStart.y);
ctx.bezierCurveTo(path.cpStart.x, path.cpStart.y, path.cpEnd.x, path.cpEnd.y, path.pEnd.x, path.pEnd.y);
ctx.closePath();
this.fillCrayon(ctx);
}
},
fillCrayon: function (ctx) {
// Set fill Style and fill shape
ctx.fillStyle = this.texturePattern;
ctx.save();
var rand = Math.random();
ctx.rotate(rand * 2 * Math.PI);
ctx.translate(rand * 10, rand * 10);
ctx.fill();
ctx.restore();
},
update: function (painter) {
this._color = painter.color.copy();
if (this.texture) {
this.texture.update(this._color, this._density);
}
},
setTexture: function (textureImage) {
this.texture = new AppNS.Painter.Graphics.Texture(textureImage);
this.texture.update(this._color, this._density);
}
}
);
WinJS.Namespace.defineWithParent(AppNS, "Painter.Tools", {
Crayon: Crayon
});
})(Microsoft.Paint);

View file

@ -0,0 +1,101 @@
////////////////////////////////////////////////////////////
//// © Microsoft. All rights reserved. ////
////////////////////////////////////////////////////////////
(function (AppNS) {
'use strict';
// The Marker tool is the simplest tool.
// It simply connect users points smoothly with a solid color, usually slightly transparent.
var Eraser = WinJS.Class.define(
// Constructor
function () {
this.autoWidth = false; // Whether or not width is based on path touch-box data or manual width value
this.width = 20; // The default line manual width
},
// Variables and Methods
{
// Begins rendering the beginning of the path
renderStart: function (path, painter) {
var ctx = painter.canvas.context;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.shadowBlur = 0;
ctx.globalCompositeOperation = "destination-out";
ctx.globalAlpha = 1;
ctx.fillStyle = "rgba(0,0,0,1)";
// Start effect
var x = path.p3.x;
var y = path.p3.y;
painter.canvas.drawCircle(x, y, this.width / 2);
},
// Renders the given path using the current Marker style
render: function (path, painter) {
if (path.drawable) {
var ctx = painter.canvas.context;
// Get start and end widths for the stroke part
var wStart = this.width;
var wEnd = this.width;
if (this.autoWidth) {
path.calculateWidth();
wStart = path.wStart;
wEnd = path.wEnd;
}
// Start at first point of shape, then bezier to second
ctx.beginPath();
path.calculateBezier(wStart / 2, wEnd / 2, 0.5);
ctx.moveTo(path.pStart.x, path.pStart.y);
ctx.bezierCurveTo(path.cpStart.x, path.cpStart.y, path.cpEnd.x, path.cpEnd.y, path.pEnd.x, path.pEnd.y);
// Line to third point of shape, then bezier to fourth, then close the path
path.calculateBezier(-1 * wStart / 2, -1 * wEnd / 2, 0.5);
ctx.lineTo(path.pEnd.x, path.pEnd.y);
ctx.bezierCurveTo(path.cpEnd.x, path.cpEnd.y, path.cpStart.x, path.cpStart.y, path.pStart.x, path.pStart.y);
ctx.closePath();
ctx.fill();
}
},
// Begins rendering the beginning of the path
renderStop: function (path, painter) {
var ctx = painter.canvas.context;
// Get start and end widths for the stroke part
var wStart = this.width;
var wEnd = this.width;
if (this.autoWidth){
path.calculateWidth();
wStart = path.wStart;
wEnd = path.wEnd;
}
// Start at first point of shape, then bezier to second
ctx.beginPath();
path.calculateEndBezier(wStart / 2, wEnd / 2);
ctx.moveTo(path.pStart.x, path.pStart.y);
ctx.bezierCurveTo(path.cpStart.x, path.cpStart.y, path.cpEnd.x, path.cpEnd.y, path.pEnd.x, path.pEnd.y);
// Line to third point of shape, then bezier to fourth, then close the path
path.calculateEndBezier(-1 * wStart / 2, -1 * wEnd / 2);
ctx.lineTo(path.pEnd.x, path.pEnd.y);
ctx.bezierCurveTo(path.cpEnd.x, path.cpEnd.y, path.cpStart.x, path.cpStart.y, path.pStart.x, path.pStart.y);
ctx.closePath();
ctx.fill();
painter.canvas.drawCircle(path.p3.x, path.p3.y, this.width / 2);
},
}
);
WinJS.Namespace.defineWithParent(AppNS, "Painter.Tools", {
Eraser: Eraser
});
})(Microsoft.Paint);

View file

@ -0,0 +1,108 @@
////////////////////////////////////////////////////////////
//// © Microsoft. All rights reserved. ////
////////////////////////////////////////////////////////////
(function (AppNS) {
'use strict';
// The Marker tool is the simplest tool.
// It simply connect users points smoothly with a solid color, usually slightly transparent.
var Marker = WinJS.Class.define(
// Constructor
function () {
this.opacity = 0.85; // The default opacity is 85%
this.autoWidth = false; // Whether or not width is based on path touch-box data or manual width value
this.width = 30; // The default line manual width is 1px
},
// Variables and Methods
{
// Renders the beginning of the path
renderStart: function (path, painter) {
var ctx = painter.canvas.context;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.shadowBlur = 0;
ctx.globalCompositeOperation = "source-over";
ctx.globalAlpha = this.opacity;
ctx.fillStyle = painter.color.toString();
},
// Renders the given path using the current Marker style
render: function (path, painter) {
var ctx = painter.canvas.context;
// Get start and end widths for the stroke part
var wStart = this.width;
var wEnd = this.width;
if (this.autoWidth){
path.calculateWidth();
wStart = path.wStart;
wEnd = path.wEnd;
}
// If just starting the path, draw semicircle
if (path.starting) {
path.calculateStartCurve(wEnd / 2, wEnd / 2, 3, wEnd, Math.pow(this.opacity, 2) * 0.22);
ctx.beginPath();
ctx.moveTo(path.pStart.x, path.pStart.y);
ctx.bezierCurveTo(path.cpStart.x, path.cpStart.y, path.cpEnd.x, path.cpEnd.y, path.pEnd.x, path.pEnd.y);
ctx.closePath();
ctx.fill();
}
if (path.drawable) {
// Start at first point of shape, then bezier to second
ctx.beginPath();
path.calculateBezier(wStart / 2, wEnd / 2, Math.pow(this.opacity, 2) * 0.22);
ctx.moveTo(path.pStart.x, path.pStart.y);
ctx.bezierCurveTo(path.cpStart.x, path.cpStart.y, path.cpEnd.x, path.cpEnd.y, path.pEnd.x, path.pEnd.y);
// Line to third point of shape, then bezier to fourth, then close the path
path.calculateBezier(-1 * wStart / 2, -1 * wEnd / 2, Math.pow(this.opacity, 2) * 0.22);
ctx.lineTo(path.pEnd.x, path.pEnd.y);
ctx.bezierCurveTo(path.cpEnd.x, path.cpEnd.y, path.cpStart.x, path.cpStart.y, path.pStart.x, path.pStart.y);
ctx.closePath();
// Set fill Style and fill shape
ctx.fill();
}
},
// Renders the end of the path with the current style
renderStop: function (path, painter) {
var ctx = painter.canvas.context;
// Get start and end widths for the stroke part
var wStart = this.width;
var wEnd = this.width;
if (this.autoWidth){
path.calculateWidth();
wStart = path.wStart;
wEnd = path.wEnd;
}
// If haven't drawn starting effect yet
if (path.created || path.starting) {
painter.canvas.drawCircle(path.p3.x, path.p3.y, wEnd / 2);
} else {
// Otherwise, draw a curve to end the path
path.calculateStopCurve(wEnd / 2, wEnd / 2, 3, wEnd, Math.pow(this.opacity, 2) * 0.22);
ctx.beginPath();
ctx.moveTo(path.pStart.x, path.pStart.y);
ctx.bezierCurveTo(path.cpStart.x, path.cpStart.y, path.cpEnd.x, path.cpEnd.y, path.pEnd.x, path.pEnd.y);
ctx.closePath();
ctx.fill();
}
}
}
);
WinJS.Namespace.defineWithParent(AppNS, "Painter.Tools", {
Marker: Marker
});
})(Microsoft.Paint);

View file

@ -0,0 +1,105 @@
////////////////////////////////////////////////////////////
//// © Microsoft. All rights reserved. ////
////////////////////////////////////////////////////////////
(function (AppNS) {
'use strict';
// The Neon tool has a glow effect
var Neon = WinJS.Class.define(
// Constructor
function () {
this.autoWidth = false; // Whether or not width is based on path touch-box data or manual width value
this.width = 20; // The default line manual width
this.glow = 20; // The amount of shadow blur used to generate the glow
},
// Variables and Methods
{
// Begins rendering the beginning of the path
renderStart: function (path, painter) {
var ctx = painter.canvas.context;
ctx.fillStyle = painter.color.toString();
ctx.shadowColor = painter.color.toString();
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.shadowBlur = this.glow;
ctx.globalCompositeOperation = "source-over";
ctx.globalAlpha = 1.0;
},
// Renders the given path using the current style
render: function (path, painter) {
var ctx = painter.canvas.context;
// Get start and end widths for the stroke part
var wStart = this.width;
var wEnd = this.width;
if (this.autoWidth) {
path.calculateWidth();
wStart = path.wStart;
wEnd = path.wEnd;
}
// If just starting the path, draw semicircle
if (path.starting) {
path.calculateStartCurve(wEnd / 2, wEnd / 2, 3, wEnd);
ctx.beginPath();
ctx.moveTo(path.pStart.x, path.pStart.y);
ctx.bezierCurveTo(path.cpStart.x, path.cpStart.y, path.cpEnd.x, path.cpEnd.y, path.pEnd.x, path.pEnd.y);
ctx.closePath();
ctx.fill();
}
if (path.drawable) {
// Start at first point of shape, then bezier to second
ctx.beginPath();
path.calculateBezier(wStart / 2, wEnd / 2, 0.5);
ctx.moveTo(path.pStart.x, path.pStart.y);
ctx.bezierCurveTo(path.cpStart.x, path.cpStart.y, path.cpEnd.x, path.cpEnd.y, path.pEnd.x, path.pEnd.y);
// Line to third point of shape, then bezier to fourth, then close the path
path.calculateBezier(-wStart / 2, -wEnd / 2, 0.5);
ctx.lineTo(path.pEnd.x, path.pEnd.y);
ctx.bezierCurveTo(path.cpEnd.x, path.cpEnd.y, path.cpStart.x, path.cpStart.y, path.pStart.x, path.pStart.y);
ctx.closePath();
ctx.fill();
}
},
// Renders the end of the path with the current style
renderStop: function (path, painter) {
var ctx = painter.canvas.context;
// Get start and end widths for the stroke part
var wStart = this.width;
var wEnd = this.width;
if (this.autoWidth){
path.calculateWidth();
wStart = path.wStart;
wEnd = path.wEnd;
}
// If haven't drawn starting effect yet
if (path.created || path.starting) {
painter.canvas.drawCircle(path.p3.x, path.p3.y, wEnd / 2);
} else {
// Otherwise, draw a curve to end the path
path.calculateStopCurve(wEnd / 2, wEnd / 2, 3, wEnd);
ctx.beginPath();
ctx.moveTo(path.pStart.x, path.pStart.y);
ctx.bezierCurveTo(path.cpStart.x, path.cpStart.y, path.cpEnd.x, path.cpEnd.y, path.pEnd.x, path.pEnd.y);
ctx.closePath();
ctx.fill();
}
}
}
);
WinJS.Namespace.defineWithParent(AppNS, "Painter.Tools", {
Neon: Neon
});
})(Microsoft.Paint);

View file

@ -0,0 +1,221 @@
////////////////////////////////////////////////////////////
//// © Microsoft. All rights reserved. ////
////////////////////////////////////////////////////////////
(function (AppNS) {
'use strict';
// The Pipe tool has a 3D look to it
var Pipe = WinJS.Class.define(
// Constructor
function () {
this.autoWidth = false; // Whether or not width is based on path touch-box data or manual width value
this.width = 20; // The default line manual width
this.height = 6; // The 'height' of the 3D pipe look, in pixels
this.maxHeight = 6;
this.numBristles = 0; // Number of bristles on each side of the pipe
this.bristles = []; // All of the pipe bristles
this.pxPerBristle = 1; // The width of each bristle in pixels
this.middleBristle = new AppNS.Painter.Tools.Bristle();
},
// Variables and Methods
{
// Begins rendering the beginning of the path
renderStart: function (path, painter) {
var color = new AppNS.Painter.Graphics.Color();
color.fromColor(painter.color);
// If the color is dark, we lighten it
if (color.red + color.green + color.blue < 255 * 3 / 2) {
color.lighten(0.2);
}
// Inner Bristles
this.numBristles = Math.floor(0.25 * this.width / this.pxPerBristle);
if (this.numBristles <= 0) {
this.numBristles = 1;
}
for (var i = 0; i < this.numBristles; i++) {
this.bristles[i] = new AppNS.Painter.Tools.Bristle();
this.bristles[i].color.fromColor(color);
this.bristles[i].color.scale(1 - ((i + 1) / this.numBristles) * (this.height / this.maxHeight));
this.bristles[i].width = this.pxPerBristle;
}
// Large inner bristle
this.middleBristle.width = this.width - (this.numBristles * this.pxPerBristle * 2);
this.middleBristle.color.fromColor(color);
var ctx = painter.canvas.context;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.shadowBlur = 0;
ctx.globalCompositeOperation = "source-over";
ctx.globalAlpha = 1.0;
},
// Renders the given path using the current style
render: function (path, painter) {
var ctx = painter.canvas.context;
// Get start and end widths for the stroke part
var wStart = this.width;
var wEnd = this.width;
if (this.autoWidth) {
path.calculateWidth();
wStart = path.wStart;
wEnd = path.wEnd;
}
var outerStart = this.middleBristle.width / 2;
var outerEnd = this.middleBristle.width / 2;
if (path.starting) {
// Draw each simulated bristle as a bezier curve
for (var i = this.numBristles - 1; i >= 0; i--) {
var offsetStart = (i + 0.5) * this.pxPerBristle + this.middleBristle.width / 2;
var offsetEnd = (i + 0.5) * this.pxPerBristle + this.middleBristle.width / 2;
ctx.strokeStyle = this.bristles[i].color.toString();
ctx.fillStyle = this.bristles[i].color.toString();
ctx.lineWidth = this.bristles[i].width;
// Draw the Bristle
path.calculateStartCurve(offsetStart, offsetEnd, 3, wEnd);
ctx.beginPath();
ctx.moveTo(path.pStart.x, path.pStart.y);
ctx.bezierCurveTo(path.cpStart.x, path.cpStart.y, path.cpEnd.x, path.cpEnd.y, path.pEnd.x, path.pEnd.y);
ctx.closePath();
ctx.fill();
}
// Draw middle
path.calculateStartCurve(this.middleBristle.width / 2, this.middleBristle.width / 2, 3, wEnd);
ctx.beginPath();
ctx.moveTo(path.pStart.x, path.pStart.y);
ctx.bezierCurveTo(path.cpStart.x, path.cpStart.y, path.cpEnd.x, path.cpEnd.y, path.pEnd.x, path.pEnd.y);
ctx.closePath();
ctx.fillStyle = this.middleBristle.color.toString();
ctx.fill();
}
if (path.drawable) {
ctx.lineWidth = this.pxPerBristle + 0.5;
// Draw each simulated bristle as a bezier curve
for (var i = this.numBristles - 1; i >= 0; i--) {
var offsetStart = (i + 0.5) * this.pxPerBristle + outerStart;
var offsetEnd = (i + 0.5) * this.pxPerBristle + outerEnd;
var overlap = 1.5 * (1 - i / this.numBristles);
// Draw the middle bristle.
// Start at first point of shape, then bezier to second.
ctx.beginPath();
path.calculateBezier(offsetStart, offsetEnd, overlap);
ctx.moveTo(path.pStart.x, path.pStart.y);
ctx.bezierCurveTo(path.cpStart.x, path.cpStart.y, path.cpEnd.x, path.cpEnd.y, path.pEnd.x, path.pEnd.y);
// Line to third point of shape, then bezier to fourth, then close the path
path.calculateBezier(-offsetStart, -offsetEnd, overlap);
ctx.lineTo(path.pEnd.x, path.pEnd.y);
ctx.bezierCurveTo(path.cpEnd.x, path.cpEnd.y, path.cpStart.x, path.cpStart.y, path.pStart.x, path.pStart.y);
ctx.closePath();
// Set fill Style and fill shape
ctx.fillStyle = this.bristles[i].color.toString();
ctx.fill();
}
// Draw the middle bristle.
// Start at first point of shape, then bezier to second.
ctx.beginPath();
path.calculateBezier(outerStart, outerEnd, 2);
ctx.moveTo(path.pStart.x, path.pStart.y);
ctx.bezierCurveTo(path.cpStart.x, path.cpStart.y, path.cpEnd.x, path.cpEnd.y, path.pEnd.x, path.pEnd.y);
// Line to third point of shape, then bezier to fourth, then close the path
path.calculateBezier(-outerStart, -outerEnd, 2);
ctx.lineTo(path.pEnd.x, path.pEnd.y);
ctx.bezierCurveTo(path.cpEnd.x, path.cpEnd.y, path.cpStart.x, path.cpStart.y, path.pStart.x, path.pStart.y);
ctx.closePath();
// Set fill Style and fill shape
ctx.fillStyle = this.middleBristle.color.toString();
ctx.fill();
}
},
// Renders the end of the path with the current style
renderStop: function (path, painter) {
var ctx = painter.canvas.context;
// Get start and end widths for the stroke part
var wStart = this.width;
var wEnd = this.width;
if (this.autoWidth){
path.calculateWidth();
wStart = path.wStart;
wEnd = path.wEnd;
}
// If haven't drawn starting effect yet
if (path.created || path.starting) {
var x = path.p3.x;
var y = path.p3.y;
// Draw bristles
for (var i = this.numBristles - 1; i >= 0; i--) {
var radius = (i + 0.5) * this.pxPerBristle + this.middleBristle.width / 2;
// Draw the Bristle
ctx.fillStyle = this.bristles[i].color.toString();
painter.canvas.drawCircle(x, y, radius);
}
// Draw middle
ctx.fillStyle = this.middleBristle.color.toString();
painter.canvas.drawCircle(x, y, this.middleBristle.width / 2);
} else { // Otherwise, draw a curve to end the path
// Draw each simulated bristle as a bezier curve
for (var i = this.numBristles - 1; i >= 0; i--) {
var offsetStart = (i + 0.5) * this.pxPerBristle + this.middleBristle.width / 2;
var offsetEnd = (i + 0.5) * this.pxPerBristle + this.middleBristle.width / 2;
var overlap = 1.5 * (1 - i / this.numBristles);
ctx.fillStyle = this.bristles[i].color.toString();
ctx.lineWidth = this.bristles[i].width;
// Draw the Bristle
path.calculateStopCurve(offsetStart, offsetEnd, 3, wEnd, overlap);
ctx.beginPath();
ctx.moveTo(path.pStart.x, path.pStart.y);
ctx.bezierCurveTo(path.cpStart.x, path.cpStart.y, path.cpEnd.x, path.cpEnd.y, path.pEnd.x, path.pEnd.y);
ctx.closePath();
ctx.fill();
}
// Draw middle
path.calculateStopCurve(this.middleBristle.width / 2, this.middleBristle.width / 2, 3, wEnd, 2);
ctx.beginPath();
ctx.moveTo(path.pStart.x, path.pStart.y);
ctx.bezierCurveTo(path.cpStart.x, path.cpStart.y, path.cpEnd.x, path.cpEnd.y, path.pEnd.x, path.pEnd.y);
ctx.closePath();
ctx.fillStyle = this.middleBristle.color.toString();
ctx.fill();
}
}
}
);
WinJS.Namespace.defineWithParent(AppNS, "Painter.Tools", {
Pipe: Pipe
});
})(Microsoft.Paint);

View file

@ -0,0 +1,133 @@
#command_toolbar_clear
{
border: none;
}
.win-commandBar-stack-horiz button
{
border: none;
font-family: "Segoe UI";
font-size: 9pt;
width: 100px;
}
.win-commandBar-stack-horiz button:hover
{
background: rgb(17,17,17);
}
.win-commandBar-rule-horiz
{
display: none;
}
.win-commandBar-horiz
{
margin-left: 30px;
}
#brush-flyout
{
background: rgb(242, 242, 222);
border: solid 2px rgb(17,17,17);
border-bottom: none;
color: rgb(17,17,17);
margin: 0 !important;
padding: 0;
width: 260px;
}
#size-flyout
{
background: rgb(242, 242, 222);
border: solid 2px rgb(17,17,17);
border-bottom: none;
color: rgb(17,17,17);
margin: 0 !important;
padding: 0;
width: 260px;
}
#option-flyout
{
background: rgb(242, 242, 222);
border: solid 2px rgb(17,17,17);
border-bottom: none;
color: rgb(17,17,17);
margin: 0 !important;
padding: 0;
width: 260px;
}
.toolbar-flyout
{
background: rgb(242, 242, 222);
border: solid 2px rgb(17,17,17);
border-bottom: none;
color: rgb(17,17,17);
margin: 0 !important;
width: 260px;
}
.brush-preview
{
height: 50px;
width: 100%;
}
.selectedPreview
{
background: #CFDDE3;
}
.brush-width-thin
{
height: 50px;
width: 260px;
}
.brush-width-thin canvas
{
height: 100%;
width: 100%;
}
.brush-width-thick
{
height: 100px;
width: 260px;
}
.colorpickerContainer
{
margin-left: 30px;
margin-right: 17px;
}
.pan-mode-toggle
{
color: #212121;
display: none;
font-family: "Segoe UI Semilight";
font-size: 20pt;
height: 50px;
text-align: center;
vertical-align: middle;
}
.toolbar-flyout-background
{
background-color: rgba(0, 0, 0, 0);
bottom: 0;
position: absolute;
z-index: 1002;
}
.canvas-flyout-background
{
background-color: rgba(0, 0, 0, 0);
left: 0;
position: absolute;
top: 0;
z-index: 1000;
}

View file

@ -0,0 +1,42 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=10" />
<title>Toolbar</title>
<link rel="stylesheet" href="/JS/CommandBar/CommandBar.css" />
<link rel="stylesheet" href="/JS/CommandBar/Flyout.css" />
<link rel="stylesheet" href="/Controls/ColorPicker/ColorPicker.css" />
<link rel="stylesheet" href="Toolbar.css" />
<script src="/WinJS/js/base.js"></script>
<script src="/WinJS/js/ui.js"></script>
<script src="/JS/Global.js"></script>
<script src="/JS/CommandBar/Flyout.js"></script>
<script src="/JS/CommandBar/CommandBar.js"></script>
<script src="/Controls/ColorPicker/ColorPicker.js"></script>
<script src="/Controls/Paint/Tools/Bristle.js"></script>
<script src="/Controls/Paint/Tools/Eraser.js"></script>
<script src="/Controls/Paint/Tools/Brush.js"></script>
<script src="/Controls/Paint/Painter.js"></script>
<script src="ToolbarColors.js"></script>
<script src="Toolbar.js"></script>
</head>
<body>
<div id="brush-flyout" data-win-control="WinJS.UI.Flyout" data-win-options="{ autoHide: 0, lightDismiss: false }">
</div>
<div id="size-flyout" data-win-control="WinJS.UI.Flyout" data-win-options="{ autoHide: 0, lightDismiss: false }">
</div>
<div id="option-flyout" data-win-control="WinJS.UI.Flyout" data-win-options="{ autoHide: 0, lightDismiss: false }">
</div>
<div id="more-flyout" data-win-control="WinJS.UI.Flyout" data-win-options="{ autoHide: 0, lightDismiss: false }">
<button id="undo-button">Undo</button> <!-- TODO: Win8Apps WORK 92: Add localization support -->
<button id="clear-button">Clear</button> <!-- TODO: Win8Apps WORK 92: Add localization support -->
</div>
<div id="appBar" class="appBar" data-win-control="WinJS.UI.AppBar" data-win-options="{ position: 'bottom', transient: true, autoHide: 0, lightDismiss: false }">
<div class="commandBar">
</div>
</div>
</body>
</html>

1092
Controls/Toolbar/Toolbar.js Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,42 @@
////////////////////////////////////////////////////////////
//// © Microsoft. All rights reserved. ////
////////////////////////////////////////////////////////////
(function (BaseNS) {
WinJS.Namespace.defineWithParent(BaseNS, "Paint", {
ToolbarManager: {
ToolbarColors: [
"rgb(0, 0, 0)",
"rgb(128, 128, 128)",
"rgb(166, 166, 166)",
"rgb(217, 217, 217)",
"rgb(255, 255, 255)",
"rgb(253, 233, 217)",
"rgb(255, 204, 153)",
"rgb(153, 102, 51)",
"rgb(102, 51, 0)",
"rgb(128, 0, 0)",
"rgb(255, 0, 0)",
"rgb(255, 102, 0)",
"rgb(255, 192, 0)",
"rgb(255, 215, 47)",
"rgb(255, 255, 0)",
"rgb(255, 255, 153)",
"rgb(204, 255, 102)",
"rgb(153, 255, 151)",
"rgb(0, 204, 0)",
"rgb(0, 128, 0)",
"rgb(51, 153, 102)",
"rgb(0, 176, 240)",
"rgb(0, 0, 255)",
"rgb(0, 0, 153)",
"rgb(79, 0, 158)",
"rgb(112, 48, 160)",
"rgb(153, 102, 255)",
"rgb(255, 153, 255)",
"rgb(255, 51, 153)",
"rgb(204, 0, 153)"
]
}
});
})(Microsoft);