Skip to content

Commit

Permalink
Draw a grid behind the main canvas
Browse files Browse the repository at this point in the history
  • Loading branch information
shannonlui committed May 28, 2022
1 parent 969ad48 commit 66f9d20
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 37 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

Pixel Artify is a web-based tool for transforming images into pixel art. Try it out at [https://pixelartify.com](https://pixelartify.com)!

![Demo Screenshot](client/src/assets/images/demo-screenshot.png)


## Getting Started

Expand Down
Binary file added client/src/assets/images/demo-screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
64 changes: 27 additions & 37 deletions client/src/pages/ImageEditor/Canvas/Canvas.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import Pica from 'pica';

import styles from './Canvas.module.css';
import * as actions from '../../../store/actions';
import { getColorDifference, convertRgbToHex } from '../../../utils/colorDifference';
import { getClosestColor, convertRgbToHex, adjustImageColors } from '../../../utils/colorDifference';
import { TOOL_TYPES } from '../../../constants/constants';
import { isDevEnv } from '../../../utils/utility';

Expand All @@ -17,6 +17,7 @@ class Canvas extends Component {
img: null,
isPainting: false
};
this.grid = React.createRef();
this.canvas = React.createRef();
this.mouseLayer = React.createRef();
}
Expand All @@ -27,6 +28,7 @@ class Canvas extends Component {

this.props.origImg.onload = () => {
this.resizeImage(this.props.origImg);
this.drawGrid();
}

// Add listeners for painting on the canvas using a mouse (from user input)
Expand Down Expand Up @@ -57,7 +59,7 @@ class Canvas extends Component {
resetCanvas = () => {
// Redraw the pixelated image. This will clear any painting done by the user!
this.pixelate(this.state.img, this.props.pixelSize);
this.adjustColors();
this.applyColorAdjustments();
}

handeStartPainting = (event) => {
Expand Down Expand Up @@ -136,7 +138,7 @@ class Canvas extends Component {
resizeImage = (img) => {
const canvas = this.canvas.current;
// TODO: do not hardcode these numbers here
const isMobile = window.innerWidth <= 700
const isMobile = window.innerWidth <= 700;
const maxWidth = isMobile ? (window.innerWidth - 20) : (window.innerWidth - 340);
const maxHeight = isMobile ? 280 : (window.innerHeight - 20);
if (img.width > maxWidth || img.height > maxHeight) {
Expand All @@ -151,7 +153,7 @@ class Canvas extends Component {
}

setCanvasSize = (width, height) => {
const canvases = [this.canvas.current, this.mouseLayer.current];
const canvases = [this.canvas.current, this.mouseLayer.current, this.grid.current];
for (var c of canvases) {
c.width = width;
c.height = height;
Expand All @@ -168,6 +170,23 @@ class Canvas extends Component {
}
}

drawGrid = () => {
const { pixelSize } = this.props;
const canvas = this.grid.current;
const ctx = canvas.getContext('2d');
const cols = Math.floor(canvas.width, pixelSize);
const rows = Math.floor(canvas.height, pixelSize);
// Clear the canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw a checkerboard pattern to be the background of the main canvas
for (let y = 0; y < rows; y++) {
for (let x = 0; x < cols; x++) {
ctx.fillStyle = ["#888", "#666"][(x + y) % 2]; // Alternate the fill color
ctx.fillRect(x * pixelSize, y * pixelSize, pixelSize, pixelSize);
}
}
}

pixelate = (img, pixelSize) => {
const canvas = this.canvas.current;
const ctx = canvas.getContext('2d');
Expand All @@ -194,7 +213,7 @@ class Canvas extends Component {
let alpha = imgData[pos + 3];
if (alpha > 0) alpha = 255;
if (this.props.palette && this.props.palette.length > 0) {
[red, green, blue] = this.getClosestColor(this.props.palette, [red, green, blue]);
[red, green, blue] = getClosestColor(this.props.palette, [red, green, blue]);
}
ctx.fillStyle = `rgba(${red}, ${green}, ${blue}, ${alpha})`;
ctx.fillRect(x, y, pixelSize, pixelSize);
Expand All @@ -215,47 +234,18 @@ class Canvas extends Component {
}
}

getClosestColor(colors, target) {
let minDiff = Number.MAX_SAFE_INTEGER;
let closest = null;
const length = colors.length;
for (let i = 0; i < length; i++) {
let diff = getColorDifference(target, colors[i]);
if (diff <= minDiff) {
minDiff = diff;
closest = colors[i];
}
}
return closest;
}

adjustColors = () => {
applyColorAdjustments = () => {
const canvas = this.canvas.current;
const ctx = canvas.getContext('2d');
const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const d = imgData.data;

const saturation = (this.props.saturation / 100) + 1;
const brightness = this.props.brightness * 0.75;
const contrast = (this.props.contrast / 100) + 1;
const con = 128 * (1 - contrast);

// Adjust the saturation, brightness, and contrast of each pixel
for (let i = 0; i < d.length; i += 4) {
const gray = d[i] * 0.3086 + d[i + 1] * 0.6094 + d[i + 2] * 0.0820;
const sat = gray * (1 - saturation);

d[i] = (d[i] * saturation + sat + brightness) * contrast + con;
d[i + 1] = (d[i + 1] * saturation + sat + brightness) * contrast + con;
d[i + 2] = (d[i + 2] * saturation + sat + brightness) * contrast + con;
}

adjustImageColors(imgData.data, this.props.saturation, this.props.brightness, this.props.contrast);
ctx.putImageData(imgData, 0, 0);
}

render() {
return(
<React.Fragment>
<canvas ref={this.grid} className={styles.canvas} />
<canvas ref={this.canvas} className={styles.canvas} />
<canvas ref={this.mouseLayer} className={styles.canvas} />
</React.Fragment>
Expand Down
1 change: 1 addition & 0 deletions client/src/pages/ImageEditor/ImageEditor.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
}

.content {
min-height: 300px;
height: 300px;
width: 100%;
margin-left: 0;
Expand Down
34 changes: 34 additions & 0 deletions client/src/utils/colorDifference.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
// From a given list of colors, return the color that is the most
// similar to the given target color.
export function getClosestColor(colors, target) {
let minDiff = Number.MAX_SAFE_INTEGER;
let closest = null;
const length = colors.length;
for (let i = 0; i < length; i++) {
let diff = getColorDifference(target, colors[i]);
if (diff <= minDiff) {
minDiff = diff;
closest = colors[i];
}
}
return closest;
}

export function getColorDifference(rgb1, rgb2) {
const lab1 = convertRgbToLgb(rgb1);
const lab2 = convertRgbToLgb(rgb2);
Expand Down Expand Up @@ -122,4 +138,22 @@ export function deltaE(labA, labB){
var deltaHkhsh = deltaH / (sh);
var i = deltaLKlsl * deltaLKlsl + deltaCkcsc * deltaCkcsc + deltaHkhsh * deltaHkhsh;
return i < 0 ? 0 : Math.sqrt(i);
}

// Adjust the saturation, brightness, and contrast of an image
export function adjustImageColors(img, saturation, brightness, contrast) {
const s = (saturation / 100) + 1;
const b = brightness * 0.75;
const c = (contrast / 100) + 1;
const intercept = 128 * (1 - c); // line intercept for contrast https://stackoverflow.com/a/37714937

for (let i = 0; i < img.length; i += 4) {
// Calculate grayness for adjusting saturation https://stackoverflow.com/a/34183839
const gray = img[i] * 0.3086 + img[i + 1] * 0.6094 + img[i + 2] * 0.0820;
const grayVal = gray * (1 - s);
// Update pixel values with saturation, brightness, and contrast adjusted
img[i] = (img[i] * s + grayVal + b) * c + intercept;
img[i + 1] = (img[i + 1] * s + grayVal + b) * c + intercept;
img[i + 2] = (img[i + 2] * s + grayVal + b) * c + intercept;
}
}

0 comments on commit 66f9d20

Please sign in to comment.