Skip to content

Commit 1f4c27d

Browse files
committed
Added new pixelator
1 parent c0e4b7d commit 1f4c27d

10 files changed

Lines changed: 169 additions & 61 deletions

File tree

README.md

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,11 @@ You can mix up the `layerConfigurations` order on how the images are saved by se
107107

108108
If you want to have logs to debug and see what is happening when you generate images you can set the variable `debugLogs` in the `config.js` file to true. It is false by default, so you will only see general logs.
109109

110-
If you want to play around with different blending modes, you can add a `blend: MODE.colorBurn` field to the layersOrder object. If you need a layers to have a different opacity then you can add the `opacity: 0.7` field to the layersOrder object as well. Both the `blend: MODE.colorBurn` and `opacity: 0.7` can be addes on the same layer if you want to.
110+
If you want to play around with different blending modes, you can add a `blend: MODE.colorBurn` field to the layersOrder `options` object.
111+
112+
If you need a layers to have a different opacity then you can add the `opacity: 0.7` field to the layersOrder `options` object as well.
113+
114+
To use a different metadata attribute name you can add the `displayName: "Awesome Eye Color"` to the `options` object. All options are optional and can be addes on the same layer if you want to.
111115

112116
Here is an example on how you can play around with both filter fields:
113117

@@ -118,11 +122,18 @@ const layerConfigurations = [
118122
layersOrder: [
119123
{ name: "Background" },
120124
{ name: "Eyeball" },
121-
{ name: "Eye color", blend: MODE.colorBurn },
125+
{
126+
name: "Eye color",
127+
options: {
128+
blend: MODE.destinationIn,
129+
opcacity: 0.2,
130+
displayName: "Awesome Eye Color",
131+
},
132+
},
122133
{ name: "Iris" },
123134
{ name: "Shine" },
124-
{ name: "Bottom lid", blend: MODE.overlay, opacity: 0.7 },
125-
{ name: "Top lid", opacity: 0.7 },
135+
{ name: "Bottom lid", options: { blend: MODE.overlay, opacity: 0.7 } },
136+
{ name: "Top lid" },
126137
],
127138
},
128139
];
@@ -214,36 +225,47 @@ That's it, you're done.
214225

215226
## Utils
216227

217-
### Updating baseUri for IPFS
228+
### Updating baseUri for IPFS and description
218229

219-
You might possibly want to update the baseUri after you have ran your collection. To update the baseUri simply run:
230+
You might possibly want to update the baseUri and description after you have ran your collection. To update the baseUri and description simply run:
220231

221232
```sh
222-
node utils/updateBaseUri.js
233+
npm run update_info
223234
```
224235

225236
### Generate a preview image
226237

227238
Create a preview image collage of your collection, run:
228239

229240
```sh
230-
node utils/createPreviewCollage.js
241+
npm run preview
231242
```
232243

233-
### Re-generate the \_metadata.json file
244+
### Generate pixelated images from collection
234245

235-
This util will only work if you have all the individual json files and want to re-generate the \_metadata.json file if you lost it, run:
246+
In order to convert images into pixelated images you would need a list of images that you want to convert. So run the generator first.
247+
248+
Then simply run this command:
236249

237250
```sh
238-
node utils/regenerateMetadata.js
251+
npm run pixelate
252+
```
253+
254+
All your images will be outputted in the `/build/pixel_images` directory.
255+
If you want to change the ratio of the pixelation then you can update the ratio property on the `pixelFormat` object in the `src/config.js` file. The lower the number on the left, the more pixelated the image will be.
256+
257+
```js
258+
const pixelFormat = {
259+
ratio: 5 / 128,
260+
};
239261
```
240262

241263
### Printing rarity data (Experimental feature)
242264

243265
To see the percentages of each attribute across your collection, run:
244266

245267
```sh
246-
node utils/rarityData.js
268+
npm run rarity
247269
```
248270

249271
The output will look something like this:

package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@
1313
},
1414
"scripts": {
1515
"build": "node index.js",
16-
"test": "echo \"Error: no test specified\" && exit 1"
16+
"generate": "node index.js",
17+
"rarity": "node utils/rarity.js",
18+
"preview": "node utils/preview.js",
19+
"pixelate": "node utils/pixelate.js",
20+
"update_info": "node utils/update_info.js"
1721
},
1822
"author": "Daniel Eugene Botha (HashLips)",
1923
"license": "MIT",

src/config.js

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
const path = require("path");
44
const isLocal = typeof process.pkg === "undefined";
55
const basePath = isLocal ? process.cwd() : path.dirname(process.execPath);
6-
const { MODE } = require(path.join(basePath, "src/blendMode.js"));
6+
const { MODE } = require("../constants/blend_mode.js");
77
const description =
88
"This is the description of your NFT project, remember to replace this";
99
const baseUri = "ipfs://NewUriToReplace";
@@ -12,9 +12,22 @@ const layerConfigurations = [
1212
{
1313
growEditionSizeTo: 10,
1414
layersOrder: [
15-
{ name: "Background" },
15+
{
16+
name: "Background",
17+
options: {
18+
blend: MODE.destinationIn,
19+
opcacity: 0.4,
20+
displayName: "BackGround Extra",
21+
},
22+
},
1623
{ name: "Eyeball" },
17-
{ name: "Eye color" },
24+
{
25+
name: "Eye color",
26+
options: {
27+
blend: MODE.colorBurn,
28+
displayName: "Awesome Color",
29+
},
30+
},
1831
{ name: "Iris" },
1932
{ name: "Shine" },
2033
{ name: "Bottom lid" },
@@ -32,6 +45,10 @@ const format = {
3245
height: 512,
3346
};
3447

48+
const pixelFormat = {
49+
ratio: 2 / 128,
50+
};
51+
3552
const background = {
3653
generate: true,
3754
brightness: "80%",
@@ -62,4 +79,5 @@ module.exports = {
6279
shuffleLayerConfigurations,
6380
debugLogs,
6481
extraMetadata,
82+
pixelFormat,
6583
};

src/main.js

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ const ctx = canvas.getContext("2d");
2929
var metadataList = [];
3030
var attributesList = [];
3131
var dnaList = new Set();
32-
const DNA_DELIMITER = '-';
32+
const DNA_DELIMITER = "-";
3333

3434
const buildSetup = () => {
3535
if (fs.existsSync(buildDir)) {
@@ -80,11 +80,19 @@ const getElements = (path) => {
8080
const layersSetup = (layersOrder) => {
8181
const layers = layersOrder.map((layerObj, index) => ({
8282
id: index,
83-
name: layerObj.name,
8483
elements: getElements(`${layersDir}/${layerObj.name}/`),
85-
blendMode:
86-
layerObj["blend"] != undefined ? layerObj["blend"] : "source-over",
87-
opacity: layerObj["opacity"] != undefined ? layerObj["opacity"] : 1,
84+
name:
85+
layerObj.options?.["displayName"] != undefined
86+
? layerObj.options?.["displayName"]
87+
: layerObj.name,
88+
blend:
89+
layerObj.options?.["blend"] != undefined
90+
? layerObj.options?.["blend"]
91+
: "source-over",
92+
opacity:
93+
layerObj.options?.["opacity"] != undefined
94+
? layerObj.options?.["opacity"]
95+
: 1,
8896
}));
8997
return layers;
9098
};
@@ -141,27 +149,27 @@ const loadLayerImg = async (_layer) => {
141149

142150
const drawElement = (_renderObject) => {
143151
ctx.globalAlpha = _renderObject.layer.opacity;
144-
ctx.globalCompositeOperation = _renderObject.layer.blendMode;
152+
ctx.globalCompositeOperation = _renderObject.layer.blend;
145153
ctx.drawImage(_renderObject.loadedImage, 0, 0, format.width, format.height);
146154
addAttributes(_renderObject);
147155
};
148156

149-
const constructLayerToDna = (_dna = '', _layers = []) => {
157+
const constructLayerToDna = (_dna = "", _layers = []) => {
150158
let mappedDnaToLayers = _layers.map((layer, index) => {
151159
let selectedElement = layer.elements.find(
152160
(e) => e.id == cleanDna(_dna.split(DNA_DELIMITER)[index])
153161
);
154162
return {
155163
name: layer.name,
156-
blendMode: layer.blendMode,
164+
blend: layer.blend,
157165
opacity: layer.opacity,
158166
selectedElement: selectedElement,
159167
};
160168
});
161169
return mappedDnaToLayers;
162170
};
163171

164-
const isDnaUnique = (_DnaList = new Set(), _dna = '') => {
172+
const isDnaUnique = (_DnaList = new Set(), _dna = "") => {
165173
return !_DnaList.has(_dna);
166174
};
167175

utils/pixelate.js

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
const fs = require("fs");
2+
const path = require("path");
3+
const { createCanvas, loadImage } = require("canvas");
4+
const isLocal = typeof process.pkg === "undefined";
5+
const basePath = isLocal ? process.cwd() : path.dirname(process.execPath);
6+
const buildDir = `${basePath}/build/pixel_images`;
7+
const inputDir = `${basePath}/build/images`;
8+
const { format, pixelFormat } = require(path.join(basePath, "/src/config.js"));
9+
const console = require("console");
10+
const canvas = createCanvas(format.width, format.height);
11+
const ctx = canvas.getContext("2d");
12+
13+
const buildSetup = () => {
14+
if (fs.existsSync(buildDir)) {
15+
fs.rmdirSync(buildDir, { recursive: true });
16+
}
17+
fs.mkdirSync(buildDir);
18+
};
19+
20+
const getImages = (_dir) => {
21+
try {
22+
return fs
23+
.readdirSync(_dir)
24+
.filter((item) => {
25+
let extension = path.extname(`${_dir}${item}`);
26+
if (extension == ".png" || extension == ".jpg") {
27+
return item;
28+
}
29+
})
30+
.map((i) => {
31+
return {
32+
filename: i,
33+
path: `${_dir}/${i}`,
34+
};
35+
});
36+
} catch {
37+
return null;
38+
}
39+
};
40+
41+
const loadImgData = async (_imgObject) => {
42+
return new Promise(async (resolve) => {
43+
const image = await loadImage(`${_imgObject.path}`);
44+
resolve({ imgObject: _imgObject, loadedImage: image });
45+
});
46+
};
47+
48+
const draw = (_imgObject) => {
49+
let size = pixelFormat.ratio;
50+
let w = canvas.width * size;
51+
let h = canvas.height * size;
52+
ctx.imageSmoothingEnabled = false;
53+
ctx.drawImage(_imgObject.loadedImage, 0, 0, w, h);
54+
ctx.drawImage(canvas, 0, 0, w, h, 0, 0, canvas.width, canvas.height);
55+
};
56+
57+
const saveImage = (_loadedImageObject) => {
58+
fs.writeFileSync(
59+
`${buildDir}/${_loadedImageObject.imgObject.filename}`,
60+
canvas.toBuffer("image/png")
61+
);
62+
};
63+
64+
const startCreating = async () => {
65+
const images = getImages(inputDir);
66+
if (images == null) {
67+
console.log("Please generate collection first.");
68+
return;
69+
}
70+
let loadedImageObjects = [];
71+
images.forEach((imgObject) => {
72+
loadedImageObjects.push(loadImgData(imgObject));
73+
});
74+
await Promise.all(loadedImageObjects).then((loadedImageObjectArray) => {
75+
loadedImageObjectArray.forEach((loadedImageObject) => {
76+
draw(loadedImageObject);
77+
saveImage(loadedImageObject);
78+
console.log(`Pixelated image: ${loadedImageObject.imgObject.filename}`);
79+
});
80+
});
81+
};
82+
83+
buildSetup();
84+
startCreating();
Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ const basePath = isLocal ? process.cwd() : path.dirname(process.execPath);
66
const fs = require("fs");
77
const layersDir = `${basePath}/layers`;
88

9-
console.log(path.join(basePath, "/src/config.js"));
109
const { layerConfigurations } = require(path.join(basePath, "/src/config.js"));
1110

1211
const { getElements } = require("../src/main.js");
@@ -35,19 +34,21 @@ layerConfigurations.forEach((config) => {
3534
};
3635
elementsForLayer.push(rarityDataElement);
3736
});
38-
37+
let layerName =
38+
layer.options?.["displayName"] != undefined
39+
? layer.options?.["displayName"]
40+
: layer.name;
3941
// don't include duplicate layers
4042
if (!rarityData.includes(layer.name)) {
4143
// add elements for each layer to chart
42-
rarityData[layer.name] = elementsForLayer;
44+
rarityData[layerName] = elementsForLayer;
4345
}
4446
});
4547
});
4648

4749
// fill up rarity chart with occurrences from metadata
4850
data.forEach((element) => {
4951
let attributes = element.attributes;
50-
5152
attributes.forEach((attribute) => {
5253
let traitType = attribute.trait_type;
5354
let value = attribute.value;

utils/regenerateMetadata.js

Lines changed: 0 additions & 31 deletions
This file was deleted.
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@ const basePath = isLocal ? process.cwd() : path.dirname(process.execPath);
66
const fs = require("fs");
77

88
console.log(path.join(basePath, "/src/config.js"));
9-
const { baseUri } = require(path.join(basePath, "/src/config.js"));
9+
const { baseUri, description } = require(path.join(basePath, "/src/config.js"));
1010

1111
// read json data
1212
let rawdata = fs.readFileSync(`${basePath}/build/json/_metadata.json`);
1313
let data = JSON.parse(rawdata);
1414

1515
data.forEach((item) => {
16+
item.description = description;
1617
item.image = `${baseUri}/${item.edition}.png`;
1718
fs.writeFileSync(
1819
`${basePath}/build/json/${item.edition}.json`,
@@ -26,3 +27,4 @@ fs.writeFileSync(
2627
);
2728

2829
console.log(`Updated baseUri for images to ===> ${baseUri}`);
30+
console.log(`Updated description for images to ===> ${description}`);

0 commit comments

Comments
 (0)