Skip to content

Commit 1e3fa1a

Browse files
authored
Merge pull request #109 from HashLips/dev
Updated
2 parents c0e4b7d + ba395a0 commit 1e3fa1a

10 files changed

Lines changed: 203 additions & 66 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: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
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(path.join(basePath, "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";
1010

1111
const layerConfigurations = [
1212
{
13-
growEditionSizeTo: 10,
13+
growEditionSizeTo: 5,
1414
layersOrder: [
1515
{ name: "Background" },
1616
{ name: "Eyeball" },
@@ -32,9 +32,28 @@ const format = {
3232
height: 512,
3333
};
3434

35+
const text = {
36+
only: false,
37+
color: "#ffffff",
38+
size: 20,
39+
xGap: 40,
40+
yGap: 40,
41+
align: "left",
42+
baseline: "top",
43+
weight: "regular",
44+
family: "Courier",
45+
spacer: " => ",
46+
};
47+
48+
const pixelFormat = {
49+
ratio: 2 / 128,
50+
};
51+
3552
const background = {
3653
generate: true,
3754
brightness: "80%",
55+
static: false,
56+
default: "#000000",
3857
};
3958

4059
const extraMetadata = {};
@@ -62,4 +81,6 @@ module.exports = {
6281
shuffleLayerConfigurations,
6382
debugLogs,
6483
extraMetadata,
84+
pixelFormat,
85+
text,
6586
};

src/main.js

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ const { createCanvas, loadImage } = require(path.join(
1111
));
1212
const buildDir = path.join(basePath, "/build");
1313
const layersDir = path.join(basePath, "/layers");
14-
console.log(path.join(basePath, "/src/config.js"));
1514
const {
1615
format,
1716
baseUri,
@@ -23,13 +22,14 @@ const {
2322
shuffleLayerConfigurations,
2423
debugLogs,
2524
extraMetadata,
25+
text,
2626
} = require(path.join(basePath, "/src/config.js"));
2727
const canvas = createCanvas(format.width, format.height);
2828
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
};
@@ -103,7 +111,7 @@ const genColor = () => {
103111
};
104112

105113
const drawBackground = () => {
106-
ctx.fillStyle = genColor();
114+
ctx.fillStyle = background.static ? background.default : genColor();
107115
ctx.fillRect(0, 0, format.width, format.height);
108116
};
109117

@@ -139,29 +147,51 @@ const loadLayerImg = async (_layer) => {
139147
});
140148
};
141149

142-
const drawElement = (_renderObject) => {
150+
const addText = (_sig, x, y, size) => {
151+
ctx.fillStyle = text.color;
152+
ctx.font = `${text.weight} ${size}pt ${text.family}`;
153+
ctx.textBaseline = text.baseline;
154+
ctx.textAlign = text.align;
155+
ctx.fillText(_sig, x, y);
156+
};
157+
158+
const drawElement = (_renderObject, _index, _layersLen) => {
143159
ctx.globalAlpha = _renderObject.layer.opacity;
144-
ctx.globalCompositeOperation = _renderObject.layer.blendMode;
145-
ctx.drawImage(_renderObject.loadedImage, 0, 0, format.width, format.height);
160+
ctx.globalCompositeOperation = _renderObject.layer.blend;
161+
text.only
162+
? addText(
163+
`${_renderObject.layer.name}${text.spacer}${_renderObject.layer.selectedElement.name}`,
164+
text.xGap,
165+
text.yGap * (_index + 1),
166+
text.size
167+
)
168+
: ctx.drawImage(
169+
_renderObject.loadedImage,
170+
0,
171+
0,
172+
format.width,
173+
format.height
174+
);
175+
146176
addAttributes(_renderObject);
147177
};
148178

149-
const constructLayerToDna = (_dna = '', _layers = []) => {
179+
const constructLayerToDna = (_dna = "", _layers = []) => {
150180
let mappedDnaToLayers = _layers.map((layer, index) => {
151181
let selectedElement = layer.elements.find(
152182
(e) => e.id == cleanDna(_dna.split(DNA_DELIMITER)[index])
153183
);
154184
return {
155185
name: layer.name,
156-
blendMode: layer.blendMode,
186+
blend: layer.blend,
157187
opacity: layer.opacity,
158188
selectedElement: selectedElement,
159189
};
160190
});
161191
return mappedDnaToLayers;
162192
};
163193

164-
const isDnaUnique = (_DnaList = new Set(), _dna = '') => {
194+
const isDnaUnique = (_DnaList = new Set(), _dna = "") => {
165195
return !_DnaList.has(_dna);
166196
};
167197

@@ -258,8 +288,12 @@ const startCreating = async () => {
258288
if (background.generate) {
259289
drawBackground();
260290
}
261-
renderObjectArray.forEach((renderObject) => {
262-
drawElement(renderObject);
291+
renderObjectArray.forEach((renderObject, index) => {
292+
drawElement(
293+
renderObject,
294+
index,
295+
layerConfigurations[layerConfigIndex].layersOrder.length
296+
);
263297
});
264298
debugLogs
265299
? console.log("Editions left to create: ", abstractedIndexes)

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

0 commit comments

Comments
 (0)