Skip to content

Commit d6c1602

Browse files
committed
make margin generation efficient
1 parent 2172711 commit d6c1602

File tree

7 files changed

+110
-22
lines changed

7 files changed

+110
-22
lines changed

app/components/mappings/rotate.js

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,32 @@
1-
import React, { useEffect, useRef } from 'react';
1+
import React, { useEffect, useRef, useState } from 'react';
22
import { environment } from '#store/environment';
33
import { observer } from 'mobx-react';
44
import { exportSprite } from '#formats/image';
5-
import rotSprite, { Pixels } from '#util/rotsprite';
6-
import { Input, Slider, Item } from '#ui';
5+
import rotSprite, { Pixels, addMarginToImageData, getRotateDiagonal } from '#util/rotsprite';
6+
import { Input, Slider, Item, SelectBase, Button } from '#ui';
77
import { mappingState } from './state';
88

99
export const Rotate = observer(() => {
1010
const canvasRef = useRef();
1111

12+
const [value, setValue] = useState(0);
13+
1214
useEffect(() => {
1315
const spriteCanv = exportSprite(environment.currentSprite);
1416
const spriteCtx = spriteCanv.getContext('2d');
1517

1618
const { width, height } = spriteCanv;
17-
spriteCtx.scale(2, 2)
1819

1920
const imageData = spriteCtx.getImageData(0, 0, width, height);
2021

21-
const diagonal =
22-
1 + (0 | (2 * Math.sqrt(width ** 2 / 4 + height ** 2 / 4)));
23-
24-
const xMargin = (diagonal - width) / 2;
25-
const yMargin = (diagonal - height) / 2;
22+
const { diagonal, xMargin, yMargin } = getRotateDiagonal(width, height);
2623

27-
spriteCanv.width = diagonal;
28-
spriteCanv.height = diagonal;
29-
spriteCtx.putImageData(imageData, xMargin, yMargin);
30-
31-
const spriteData = spriteCtx.getImageData(0, 0, diagonal, diagonal);
24+
const spriteData = addMarginToImageData(
25+
spriteCtx,
26+
imageData,
27+
xMargin,
28+
yMargin,
29+
);
3230

3331
const data = new Uint32Array(diagonal ** 2);
3432

@@ -64,9 +62,6 @@ export const Rotate = observer(() => {
6462
canvas.width = diagonal;
6563
canvas.height = diagonal;
6664
ctx.putImageData(rotatedData, 0, 0);
67-
canvas.style.width = '400px';
68-
canvas.style.imageRendering = 'pixelated';
69-
canvas.style.backgroundColor = 'red';
7065
}, [environment.currentSprite, mappingState.rotateAngle]);
7166

7267
const assertInput = (num) => {
@@ -79,6 +74,11 @@ export const Rotate = observer(() => {
7974
<div className="rotsprite">
8075
<Item>Rotate Sprite</Item>
8176
<canvas ref={canvasRef} />
77+
<SelectBase
78+
options={[...Array(7).keys()].map(d => String(d*90))}
79+
value={value}
80+
onChange={e => setValue(e.value)}
81+
/>
8282
<Input
8383
store={mappingState}
8484
assert={assertInput}
@@ -92,6 +92,8 @@ export const Rotate = observer(() => {
9292
store={mappingState}
9393
accessor="rotateAngle"
9494
/>
95+
<Button color="red">Reimport</Button>
96+
<Button color="magenta">close</Button>
9597
</div>
9698
);
9799
});

app/components/ui/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ export { Button } from './button';
22
export { Item } from './item';
33
export { Input } from './input';
44
export { File } from './file';
5-
export { Select } from './select';
5+
export { Select, SelectBase } from './select';
66
export { Slider } from './slider';
77
export { Checkbox } from './checkbox';

app/components/ui/select/index.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,15 @@ export const Select = observer(class Select extends Component {
6969
}
7070

7171
});
72+
73+
export function SelectBase({ label, ...props }) {
74+
return <div className="row select">
75+
{label && <span>
76+
{label}
77+
&emsp;
78+
</span>}
79+
<Dropdown
80+
{...props}
81+
/>
82+
</div>;
83+
}

app/util/rotsprite.js

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// original algorithm Xenowhirl
22
// stole code from ChaseMor/pxt-arcade-rotsprite
3-
// generated the rest with chatGPT
3+
// some generated with chatGPT
44

55
export default function rotSprite(image, angle) {
66
image = scale2xImage(image);
@@ -19,6 +19,7 @@ function scale2xImage(original) {
1919
const b = original.getPixel(x + 1, y);
2020
const c = original.getPixel(x - 1, y);
2121
const d = original.getPixel(x, y + 1);
22+
2223
if (c == a && c != d && a != b) {
2324
scaled.setPixel(x << 1, y << 1, a);
2425
} else {
@@ -43,6 +44,7 @@ function scale2xImage(original) {
4344
}
4445
return scaled;
4546
}
47+
4648
function rotateAndReduceImage(original, angle) {
4749
let rotated = Pixels.create(original.width >> 3, original.height >> 3);
4850

@@ -112,4 +114,67 @@ export class Pixels {
112114
}
113115
return clonedImage;
114116
};
117+
118+
scale = (factor) => {
119+
// Calculate the new dimensions of the scaled image
120+
const scaledWidth = this.width * factor;
121+
const scaledHeight = this.height * factor;
122+
123+
// Create a new Image object with the new dimensions
124+
const scaledImage = new Pixels(scaledWidth, scaledHeight);
125+
126+
// Loop through the pixels of the scaled image
127+
for (let y = 0; y < scaledHeight; y++) {
128+
for (let x = 0; x < scaledWidth; x++) {
129+
// Calculate the coordinates of the corresponding pixel in the original image
130+
const originalX = Math.floor(x / factor);
131+
const originalY = Math.floor(y / factor);
132+
133+
// Set the pixel in the scaled image to the nearest pixel in the original image
134+
scaledImage.setPixel(x, y, this.getPixel(originalX, originalY));
135+
}
136+
}
137+
138+
return scaledImage;
139+
};
140+
}
141+
142+
143+
export function addMarginToImageData(ctx, imageData, xMargin, yMargin) {
144+
// Create a new ImageData object with the new dimensions, including the margin.
145+
const newImageData = ctx.createImageData(
146+
imageData.width + xMargin * 2,
147+
imageData.height + yMargin * 2
148+
);
149+
150+
// Loop through the original image data and copy the pixel values to the new
151+
// ImageData object, taking the margin into account.
152+
for (let row = 0; row < imageData.height; row++) {
153+
for (let col = 0; col < imageData.width; col++) {
154+
const sourcePixel = [ imageData.data[(row * imageData.width + col) * 4 + 0],
155+
imageData.data[(row * imageData.width + col) * 4 + 1],
156+
imageData.data[(row * imageData.width + col) * 4 + 2],
157+
imageData.data[(row * imageData.width + col) * 4 + 3]
158+
];
159+
160+
const destRow = row + yMargin;
161+
const destCol = col + xMargin;
162+
for (let i = 0; i < 4; i++) {
163+
newImageData.data[(destRow * newImageData.width + destCol) * 4 + i] =
164+
sourcePixel[i];
165+
}
166+
}
167+
}
168+
169+
return newImageData;
170+
}
171+
172+
export function getRotateDiagonal(width, height) {
173+
const diagonal =
174+
1 + (0 | (2 * Math.sqrt(width ** 2 / 4 + height ** 2 / 4)));
175+
176+
const xMargin = Math.round((diagonal - width) / 2);
177+
const yMargin = Math.round((diagonal - height) / 2);
178+
179+
return { diagonal: xMargin * 2 + width, xMargin, yMargin };
115180
}

styles/components/import.scss

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@
6060

6161
canvas {
6262
z-index: 1;
63-
image-rendering: pixelated;
6463
}
6564

6665
.import-canvas {

styles/components/rotsprite.scss

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,18 @@
66
}
77
box-sizing: content-box;
88
position: absolute;
9-
z-index: 2;
9+
z-index: 3;
1010
background-color: $black;
1111
left: 15px;
1212
right: 15px;
1313
box-shadow: 2px 2px 4px $black;
1414
padding: 15px;
1515
margin-top: 100px;
16+
17+
canvas {
18+
background-image: url(../images/t.png);
19+
border: 3px solid $grey;
20+
width: 400px;
21+
height: 400px;
22+
}
1623
}

styles/main.scss

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,15 @@ a {
5252
z-index: -1;
5353
}
5454

55+
canvas {
56+
image-rendering: pixelated;
57+
}
58+
5559
.canvas-debug {
5660
position: absolute;
5761
z-index: 10;
5862
bottom: 20px;
5963
left: 20px;
60-
image-rendering: pixelated;
6164
background-color: rgba(255,0,0,0.5);
6265
}
6366

0 commit comments

Comments
 (0)