Skip to content

Commit cc16574

Browse files
committed
Updates
1 parent f6caae7 commit cc16574

14 files changed

+227
-18
lines changed

asset-manifest.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"files": {
3-
"main.css": "/static/css/main.3ee7aca4.css",
4-
"main.js": "/static/js/main.8217a071.js",
3+
"main.css": "/static/css/main.605c7201.css",
4+
"main.js": "/static/js/main.e1c5692c.js",
55
"static/js/453.f568ff1d.chunk.js": "/static/js/453.f568ff1d.chunk.js",
66
"static/media/border.jpg": "/static/media/border.b7869a9ea7ccd009873c.jpg",
77
"static/media/parchment.png": "/static/media/parchment.c4af92656f29dc66bd67.png",
@@ -10,12 +10,12 @@
1010
"static/media/slick.ttf": "/static/media/slick.c94f7671dcc99dce43e2.ttf",
1111
"static/media/slick.woff": "/static/media/slick.295183786cd8a1389865.woff",
1212
"index.html": "/index.html",
13-
"main.3ee7aca4.css.map": "/static/css/main.3ee7aca4.css.map",
14-
"main.8217a071.js.map": "/static/js/main.8217a071.js.map",
13+
"main.605c7201.css.map": "/static/css/main.605c7201.css.map",
14+
"main.e1c5692c.js.map": "/static/js/main.e1c5692c.js.map",
1515
"453.f568ff1d.chunk.js.map": "/static/js/453.f568ff1d.chunk.js.map"
1616
},
1717
"entrypoints": [
18-
"static/css/main.3ee7aca4.css",
19-
"static/js/main.8217a071.js"
18+
"static/css/main.605c7201.css",
19+
"static/js/main.e1c5692c.js"
2020
]
2121
}

index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<!doctype html><html lang="en" class="dark"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><link rel="icon" type="image/svg+xml" href="/favicon.svg"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><link rel="alternate" type="application/rss+xml" title="Fezcodex RSS Feed" href="/rss.xml"/><meta name="description" content="codex by fezcode..."/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&family=Space+Mono:wght@400;700&display=swap" rel="stylesheet"><link href="https://fonts.googleapis.com/css2?family=Arvo&family=Inter&family=Playfair+Display&display=swap" rel="stylesheet"><title>fezcodex</title><script defer="defer" src="/static/js/main.8217a071.js"></script><link href="/static/css/main.3ee7aca4.css" rel="stylesheet"></head><body class="bg-slate-950"><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
1+
<!doctype html><html lang="en" class="dark"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><link rel="icon" type="image/svg+xml" href="/favicon.svg"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><link rel="alternate" type="application/rss+xml" title="Fezcodex RSS Feed" href="/rss.xml"/><meta name="description" content="codex by fezcode..."/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&family=Space+Mono:wght@400;700&display=swap" rel="stylesheet"><link href="https://fonts.googleapis.com/css2?family=Arvo&family=Inter&family=Playfair+Display&display=swap" rel="stylesheet"><title>fezcodex</title><script defer="defer" src="/static/js/main.e1c5692c.js"></script><link href="/static/css/main.605c7201.css" rel="stylesheet"></head><body class="bg-slate-950"><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>

posts/image-toolkit-deep-dive.txt

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# Image Toolkit Deep Dive
2+
3+
In this blog post, we'll take a deep dive into the implementation of the Image Toolkit app. We'll explore the various image filters and their algorithms, and we'll also discuss a common React Hook-related warning and how to fix it.
4+
5+
You can try it here [apps::itk](/#/apps::itk)
6+
7+
## The Filters
8+
9+
The Image Toolkit app provides a variety of filters that you can apply to your images. Let's take a look at each one and the algorithm behind it.
10+
11+
### Monochrome
12+
13+
The monochrome filter converts an image to grayscale. The algorithm for this is quite simple. For each pixel in the image, we calculate the average of the red, green, and blue values. Then, we set the red, green, and blue values of the pixel to this average value.
14+
15+
```javascript
16+
const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
17+
data[i] = avg; // red
18+
data[i + 1] = avg; // green
19+
data[i + 2] = avg; // blue
20+
```
21+
22+
### Blur
23+
24+
The blur filter applies a blur effect to the image. We use the `stackblur-canvas` library to achieve this effect. The `canvasRGBA` function from this library takes the canvas, the coordinates of the area to blur, and the blur radius as input.
25+
26+
```javascript
27+
canvasRGBA(canvas, 0, 0, canvas.width, canvas.height, blurAmount);
28+
```
29+
30+
### Dithering
31+
32+
Dithering is a technique used to create the illusion of more colors than are actually available. We use the Bayer dithering algorithm. This algorithm uses a threshold map (the Bayer matrix) to determine whether a pixel should be black or white.
33+
34+
```javascript
35+
const bayerMatrix = [
36+
[1, 9, 3, 11],
37+
[13, 5, 15, 7],
38+
[4, 12, 2, 10],
39+
[16, 8, 14, 6]
40+
];
41+
const threshold = bayerMatrix[y % matrixSize][x % matrixSize] * 16;
42+
const newValue = gray < threshold ? 0 : 255;
43+
```
44+
45+
### Cel Shading
46+
47+
Cel shading is a non-photorealistic rendering technique that makes 3D computer graphics appear to be flat. To achieve this effect, we first apply color quantization to reduce the number of colors in the image. Then, we use the Sobel operator to detect the edges in the image. Finally, we combine the quantized image and the edges to create the cel-shaded effect.
48+
49+
### Halftone
50+
51+
The halftone filter simulates the effect of printing an image with a series of dots. We first convert the image to grayscale. Then, for each grid of pixels, we calculate the average brightness and draw a circle with a radius proportional to the brightness.
52+
53+
### Solarization
54+
55+
Solarization is an effect where the image is partially reversed. We set a threshold and for each pixel, if the color component is less than the threshold, we invert it.
56+
57+
```javascript
58+
if (r < threshold) data[i] = 255 - r;
59+
if (g < threshold) data[i + 1] = 255 - g;
60+
if (b < threshold) data[i + 2] = 255 - b;
61+
```
62+
63+
### Posterization
64+
65+
Posterization is a process in which the number of colors in an image is reduced. For each color component of a pixel, we round it to the nearest value in a smaller set of values.
66+
67+
### Sepia
68+
69+
The sepia filter gives the image a warm, brownish tone. We use a set of coefficients to calculate the new red, green, and blue values for each pixel.
70+
71+
```javascript
72+
data[i] = Math.min(255, (r * 0.393) + (g * 0.769) + (b * 0.189));
73+
data[i + 1] = Math.min(255, (r * 0.349) + (g * 0.686) + (b * 0.168));
74+
data[i + 2] = Math.min(255, (r * 0.272) + (g * 0.534) + (b * 0.131));
75+
```
76+
77+
### Pixelization
78+
79+
The pixelization filter creates a blocky, pixelated effect. We divide the image into a grid of blocks and fill each block with the color of the top-left pixel in that block.
80+
81+
### Duotone
82+
83+
The duotone filter uses two colors to create a two-toned image. We first convert the image to grayscale. Then, we interpolate between a dark color and a light color based on the brightness of each pixel.
84+
85+
### ASCII Art
86+
87+
The ASCII art filter converts the image to ASCII characters. We first convert the image to grayscale. Then, for each pixel, we map its brightness to a character from a character ramp.
88+
89+
```javascript
90+
const ascii = asciiArt(imageData, '@%#*+=-:. ');
91+
```
92+
93+
## The `useCallback` and `useEffect` Dependency Array Error
94+
95+
You might have encountered this warning while developing the Image Toolkit app:
96+
97+
```
98+
The 'toGrayscale' function makes the dependencies of useEffect Hook (at line 348) change on every render. To fix this, wrap the definition of 'toGrayscale' in its own useCallback() Hook
99+
```
100+
101+
This warning occurs because the `toGrayscale` function is defined inside the `ImageToolkitPage` component. This means that on every render of the component, a new `toGrayscale` function is created. Since `toGrayscale` is a dependency of the `useEffect` hook, the hook will run on every render, causing an infinite loop.
102+
103+
To fix this, we can wrap the definition of `toGrayscale` in its own `useCallback` hook. The `useCallback` hook will memoize the function, so that it is not recreated on every render.
104+
105+
```javascript
106+
const toGrayscale = useCallback((imageData) => {
107+
// ...
108+
}, []);
109+
```
110+
111+
By wrapping all the image processing functions in `useCallback`, we can prevent the `useEffect` hook from running on every render and fix the infinite loop.

posts/posts.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
11
[
2+
{
3+
"slug": "image-toolkit-deep-dive",
4+
"title": "Image Toolkit Deep Dive",
5+
"date": "2025-11-10",
6+
"updated": "2025-11-10",
7+
"description": "A deep dive into the implementation of the Image Toolkit app.",
8+
"tags": ["canvas", "react", "css", "image-processing", "tailwind"],
9+
"category": "dev",
10+
"filename": "image-toolkit-deep-dive.txt"
11+
},
212
{
313
"slug": "picker-wheel-deep-dive",
414
"title": "Picker Wheel Deep Dive",

rss.xml

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,87 @@
99
<link>https://fezcode.com</link>
1010
</image>
1111
<generator>RSS for Node</generator>
12-
<lastBuildDate>Mon, 10 Nov 2025 19:15:40 GMT</lastBuildDate>
12+
<lastBuildDate>Mon, 10 Nov 2025 21:15:53 GMT</lastBuildDate>
1313
<atom:link href="https://fezcode.com/rss.xml" rel="self" type="application/rss+xml"/>
14-
<pubDate>Mon, 10 Nov 2025 19:15:39 GMT</pubDate>
14+
<pubDate>Mon, 10 Nov 2025 21:15:53 GMT</pubDate>
1515
<copyright><![CDATA[2025 Ahmed Samil Bulbul]]></copyright>
1616
<language><![CDATA[en]]></language>
1717
<managingEditor><![CDATA[samil.bulbul@gmail.com (Ahmed Samil Bulbul)]]></managingEditor>
1818
<webMaster><![CDATA[samil.bulbul@gmail.com (Ahmed Samil Bulbul)]]></webMaster>
1919
<ttl>60</ttl>
20+
<item>
21+
<title><![CDATA[Image Toolkit Deep Dive]]></title>
22+
<description><![CDATA[[object Object]]]></description>
23+
<link>https://fezcode.com/#/blog/image-toolkit-deep-dive</link>
24+
<guid isPermaLink="false">https://fezcode.com/#/blog/image-toolkit-deep-dive</guid>
25+
<dc:creator><![CDATA[Ahmed Samil Bulbul]]></dc:creator>
26+
<pubDate>Mon, 10 Nov 2025 00:00:00 GMT</pubDate>
27+
<content:encoded><![CDATA[<h1>Image Toolkit Deep Dive</h1>
28+
<p>In this blog post, we&#39;ll take a deep dive into the implementation of the Image Toolkit app. We&#39;ll explore the various image filters and their algorithms, and we&#39;ll also discuss a common React Hook-related warning and how to fix it.</p>
29+
<p>You can try it here <a href="/#/apps::itk">apps::itk</a></p>
30+
<h2>The Filters</h2>
31+
<p>The Image Toolkit app provides a variety of filters that you can apply to your images. Let&#39;s take a look at each one and the algorithm behind it.</p>
32+
<h3>Monochrome</h3>
33+
<p>The monochrome filter converts an image to grayscale. The algorithm for this is quite simple. For each pixel in the image, we calculate the average of the red, green, and blue values. Then, we set the red, green, and blue values of the pixel to this average value.</p>
34+
<pre><code class="language-javascript">const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
35+
data[i] = avg; // red
36+
data[i + 1] = avg; // green
37+
data[i + 2] = avg; // blue
38+
</code></pre>
39+
<h3>Blur</h3>
40+
<p>The blur filter applies a blur effect to the image. We use the <code>stackblur-canvas</code> library to achieve this effect. The <code>canvasRGBA</code> function from this library takes the canvas, the coordinates of the area to blur, and the blur radius as input.</p>
41+
<pre><code class="language-javascript">canvasRGBA(canvas, 0, 0, canvas.width, canvas.height, blurAmount);
42+
</code></pre>
43+
<h3>Dithering</h3>
44+
<p>Dithering is a technique used to create the illusion of more colors than are actually available. We use the Bayer dithering algorithm. This algorithm uses a threshold map (the Bayer matrix) to determine whether a pixel should be black or white.</p>
45+
<pre><code class="language-javascript">const bayerMatrix = [
46+
[1, 9, 3, 11],
47+
[13, 5, 15, 7],
48+
[4, 12, 2, 10],
49+
[16, 8, 14, 6]
50+
];
51+
const threshold = bayerMatrix[y % matrixSize][x % matrixSize] * 16;
52+
const newValue = gray &lt; threshold ? 0 : 255;
53+
</code></pre>
54+
<h3>Cel Shading</h3>
55+
<p>Cel shading is a non-photorealistic rendering technique that makes 3D computer graphics appear to be flat. To achieve this effect, we first apply color quantization to reduce the number of colors in the image. Then, we use the Sobel operator to detect the edges in the image. Finally, we combine the quantized image and the edges to create the cel-shaded effect.</p>
56+
<h3>Halftone</h3>
57+
<p>The halftone filter simulates the effect of printing an image with a series of dots. We first convert the image to grayscale. Then, for each grid of pixels, we calculate the average brightness and draw a circle with a radius proportional to the brightness.</p>
58+
<h3>Solarization</h3>
59+
<p>Solarization is an effect where the image is partially reversed. We set a threshold and for each pixel, if the color component is less than the threshold, we invert it.</p>
60+
<pre><code class="language-javascript">if (r &lt; threshold) data[i] = 255 - r;
61+
if (g &lt; threshold) data[i + 1] = 255 - g;
62+
if (b &lt; threshold) data[i + 2] = 255 - b;
63+
</code></pre>
64+
<h3>Posterization</h3>
65+
<p>Posterization is a process in which the number of colors in an image is reduced. For each color component of a pixel, we round it to the nearest value in a smaller set of values.</p>
66+
<h3>Sepia</h3>
67+
<p>The sepia filter gives the image a warm, brownish tone. We use a set of coefficients to calculate the new red, green, and blue values for each pixel.</p>
68+
<pre><code class="language-javascript">data[i] = Math.min(255, (r * 0.393) + (g * 0.769) + (b * 0.189));
69+
data[i + 1] = Math.min(255, (r * 0.349) + (g * 0.686) + (b * 0.168));
70+
data[i + 2] = Math.min(255, (r * 0.272) + (g * 0.534) + (b * 0.131));
71+
</code></pre>
72+
<h3>Pixelization</h3>
73+
<p>The pixelization filter creates a blocky, pixelated effect. We divide the image into a grid of blocks and fill each block with the color of the top-left pixel in that block.</p>
74+
<h3>Duotone</h3>
75+
<p>The duotone filter uses two colors to create a two-toned image. We first convert the image to grayscale. Then, we interpolate between a dark color and a light color based on the brightness of each pixel.</p>
76+
<h3>ASCII Art</h3>
77+
<p>The ASCII art filter converts the image to ASCII characters. We first convert the image to grayscale. Then, for each pixel, we map its brightness to a character from a character ramp.</p>
78+
<pre><code class="language-javascript">const ascii = asciiArt(imageData, &#39;@%#*+=-:. &#39;);
79+
</code></pre>
80+
<h2>The <code>useCallback</code> and <code>useEffect</code> Dependency Array Error</h2>
81+
<p>You might have encountered this warning while developing the Image Toolkit app:</p>
82+
<pre><code>The &#39;toGrayscale&#39; function makes the dependencies of useEffect Hook (at line 348) change on every render. To fix this, wrap the definition of &#39;toGrayscale&#39; in its own useCallback() Hook
83+
</code></pre>
84+
<p>This warning occurs because the <code>toGrayscale</code> function is defined inside the <code>ImageToolkitPage</code> component. This means that on every render of the component, a new <code>toGrayscale</code> function is created. Since <code>toGrayscale</code> is a dependency of the <code>useEffect</code> hook, the hook will run on every render, causing an infinite loop.</p>
85+
<p>To fix this, we can wrap the definition of <code>toGrayscale</code> in its own <code>useCallback</code> hook. The <code>useCallback</code> hook will memoize the function, so that it is not recreated on every render.</p>
86+
<pre><code class="language-javascript">const toGrayscale = useCallback((imageData) =&gt; {
87+
// ...
88+
}, []);
89+
</code></pre>
90+
<p>By wrapping all the image processing functions in <code>useCallback</code>, we can prevent the <code>useEffect</code> hook from running on every render and fix the infinite loop.</p>
91+
<p><a href="https://fezcode.com/#/blog/image-toolkit-deep-dive">Read more...</a></p>]]></content:encoded>
92+
</item>
2093
<item>
2194
<title><![CDATA[Picker Wheel Deep Dive]]></title>
2295
<description><![CDATA[[object Object]]]></description>

static/css/main.3ee7aca4.css

Lines changed: 0 additions & 4 deletions
This file was deleted.

static/css/main.3ee7aca4.css.map

Lines changed: 0 additions & 1 deletion
This file was deleted.

static/css/main.605c7201.css

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

static/css/main.605c7201.css.map

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

static/js/main.8217a071.js

Lines changed: 0 additions & 3 deletions
This file was deleted.

0 commit comments

Comments
 (0)