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,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);