first
This commit is contained in:
commit
59426d62fb
102 changed files with 42796 additions and 0 deletions
104
Controls/Paint/Graphics/Canvas.js
Normal file
104
Controls/Paint/Graphics/Canvas.js
Normal 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);
|
||||
146
Controls/Paint/Graphics/Color.js
Normal file
146
Controls/Paint/Graphics/Color.js
Normal 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);
|
||||
294
Controls/Paint/Graphics/Path.js
Normal file
294
Controls/Paint/Graphics/Path.js
Normal 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);
|
||||
87
Controls/Paint/Graphics/Texture.js
Normal file
87
Controls/Paint/Graphics/Texture.js
Normal 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);
|
||||
77
Controls/Paint/Math/Point.js
Normal file
77
Controls/Paint/Math/Point.js
Normal 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);
|
||||
143
Controls/Paint/Math/Vector.js
Normal file
143
Controls/Paint/Math/Vector.js
Normal 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
151
Controls/Paint/Painter.js
Normal 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);
|
||||
BIN
Controls/Paint/Res/crayon_texture.png
Normal file
BIN
Controls/Paint/Res/crayon_texture.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
25
Controls/Paint/Tools/Bristle.js
Normal file
25
Controls/Paint/Tools/Bristle.js
Normal 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);
|
||||
176
Controls/Paint/Tools/Brush.js
Normal file
176
Controls/Paint/Tools/Brush.js
Normal 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);
|
||||
146
Controls/Paint/Tools/Crayon.js
Normal file
146
Controls/Paint/Tools/Crayon.js
Normal 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);
|
||||
101
Controls/Paint/Tools/Eraser.js
Normal file
101
Controls/Paint/Tools/Eraser.js
Normal 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);
|
||||
108
Controls/Paint/Tools/Marker.js
Normal file
108
Controls/Paint/Tools/Marker.js
Normal 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);
|
||||
105
Controls/Paint/Tools/Neon.js
Normal file
105
Controls/Paint/Tools/Neon.js
Normal 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);
|
||||
221
Controls/Paint/Tools/Pipe.js
Normal file
221
Controls/Paint/Tools/Pipe.js
Normal 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);
|
||||
Reference in a new issue