Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
3954a7a
feat: add px span in pad
volta2030 Nov 16, 2025
9554ac5
fix: devide to rect crop
volta2030 Nov 16, 2025
1f14322
fix: remove PRIVACYPOLICY (Never need)
volta2030 Nov 17, 2025
c5078c4
feat: add PIX moudle
volta2030 Nov 18, 2025
7f91db7
feat: add clipboard for color picker #125
volta2030 Nov 29, 2025
b757546
feat: npm update
volta2030 Nov 29, 2025
bb8389c
fix: change PIX load() name to open() & add convertToPIX()
volta2030 Nov 29, 2025
e10ce00
feat: add open & save for pix extension
volta2030 Nov 29, 2025
fa3745d
fix: change PIX module function name...
volta2030 Nov 29, 2025
896743b
fix: rename moudle import name
volta2030 Nov 29, 2025
b12f1b4
feat: add compression & label section in toPix()
volta2030 Nov 30, 2025
4c91701
feat: add padColorBox
volta2030 Nov 30, 2025
1b0db81
feat: add padColorValue
volta2030 Nov 30, 2025
20caa8a
fix: add color to pad command
volta2030 Nov 30, 2025
a2acad0
fix: including # to padColor
volta2030 Nov 30, 2025
ce7aecd
feat: enable changing padColorBox by padColorValue #126
volta2030 Nov 30, 2025
0c400b1
Initial plan
Copilot Nov 30, 2025
bacb4d1
Add HEIF/HEIC image format support
Copilot Nov 30, 2025
a86b23d
feat: add heif, heic selection into extension-combo selection
volta2030 Nov 30, 2025
2feed6f
Merge pull request #127 from Pascal-Institute/copilot/add-heif-opener…
volta2030 Nov 30, 2025
11bf7cd
Update README.md
volta2030 Nov 30, 2025
d0aeece
feat: add joint section by pix spec in toPix()
volta2030 Dec 2, 2025
07ffa05
feat: link colorBox to colorPicker #128
volta2030 Dec 2, 2025
72fbfad
Initial plan
Copilot Dec 6, 2025
8835bdf
Add rotating dotted border effect during image drag-and-drop
Copilot Dec 6, 2025
37228be
Fix code review issues: improve animation and dragleave handling
Copilot Dec 6, 2025
e35f446
Merge pull request #130 from Pascal-Institute/copilot/add-image-borde…
volta2030 Dec 6, 2025
79bab6a
feat: Version Up
volta2030 Dec 7, 2025
23af15b
Update imgkit/processing/image_loader.js
volta2030 Dec 7, 2025
3041ab7
Update imgkit/processing/image_loader.js
volta2030 Dec 7, 2025
b20ae25
Update imgkit/core/image_layer.js
volta2030 Dec 7, 2025
f86edda
fix: add return statement when pix save & add missing version up tag
volta2030 Dec 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 0 additions & 63 deletions PRIVACYPOLICY.md

This file was deleted.

4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

#### The Image Processing & Analysis Tool based on Electron framework & sharp npm package

<img width="1259" height="1007" alt="image" src="https://github.com/user-attachments/assets/c6dbe835-26d8-40ab-8fa4-a6a330e23baf" />
<img width="1257" height="1011" alt="image" src="https://github.com/user-attachments/assets/03b939c2-9f7b-40e2-8b40-d4b9ae100f31" />

## 0. Application mission : Give wings to pixels

Expand Down Expand Up @@ -66,7 +66,7 @@ npm run build
### 4-1. Availiable file extension

```bash
png svg jpg jpeg webp gif bmp ico tiff tif avif
png svg jpg jpeg webp gif bmp ico tiff tif avif heif heic pix
```

**Note:** Animated GIF files are now supported! When you load an animated GIF, playback controls appear automatically. See [GIF Animation Documentation](docs/GIF_ANIMATION.md) for details.
Expand Down
14 changes: 14 additions & 0 deletions assets/pix.pix
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"width": 8,
"height": 8,
"channel": 1,
"depth": 8,
"compression": {
"method": "none"
},
"hex_data": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"label": {
"cat": [22, 44],
"dog": [100, 103]
}
}
4 changes: 4 additions & 0 deletions css/paint_panel.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#watermarkBtn:hover,
#colorpickerBtn:hover,
#colorpickerBox:hover,
#padColorBox:hover,
#padBtn:hover,
#watermarkUploadBtn:hover {
cursor: pointer;
Expand Down Expand Up @@ -41,6 +42,7 @@
right: 0px;
}

#padColorBox,
#colorpickerBox {
margin-left: 5px;
width: 36px;
Expand All @@ -49,6 +51,7 @@
background-color: black;
}

#padIndicator,
#colorpickerIndicator {
display: flex;
align-items: center;
Expand All @@ -62,6 +65,7 @@
object-fit: contain;
}

#padColorValue,
#padValue {
width: 80px;
margin-left: 5px;
Expand Down
8 changes: 8 additions & 0 deletions imgkit/core/image_layer.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const { ImageLoader } = require("../processing/image_loader");
const { ModeManager } = require("../features/image_mode");
const { LayerHistory } = require("../features/layer_history");
const { GifAnimation } = require("../features/gif_animation");
const { Pix } = require("./pix");

const LAYER_EVENT_CHANNEL = "imgkit-layer-event";
let nextLayerId = 1;
Expand Down Expand Up @@ -493,6 +494,13 @@ class ImageLayer {
// If saving as SVG and we have SVG data, save the actual SVG
if (this.extension === "svg" && this.svgData) {
buffer = Buffer.from(this.svgData, "utf-8");
} else if (this.extension === "pix") {
// For PIX format, convert back to PIX buffer
const pixData = await Pix.toPix(this.buffer, this.info);
await Pix.save(pixData, savePath);
this.filepath = savePath;
ipcRenderer.send("showNotificationREQ", "imgkit-save");
return;
} else {
// For other formats, extract from canvas
const base64Data = this.image.src.replace(
Expand Down
4 changes: 3 additions & 1 deletion imgkit/core/main_renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,9 @@ class ImgKitRenderer {
if (!entry) continue;

const targetLayer =
i === 0 ? this.getLayerById(targetLayerId) : this.createImageLayer(false);
i === 0
? this.getLayerById(targetLayerId)
: this.createImageLayer(false);
if (!targetLayer) continue;

let success = false;
Expand Down
73 changes: 73 additions & 0 deletions imgkit/core/pix.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
const jsonfile = require("jsonfile");
const sharp = require("sharp");

class Pix {
static open(path) {
try {
const data = jsonfile.readFileSync(path);
return data;
} catch (err) {
console.error("Error reading pixData.json:", err);
return null;
}
}

static save(pixData, path) {
try {
jsonfile.writeFileSync(path, pixData, { spaces: 2 });
} catch (err) {
console.error("Error writing pixData.json:", err);
}
}

static openFromBuffer(buffer) {
try {
const text = Buffer.isBuffer(buffer) ? buffer.toString("utf-8") : buffer;
return JSON.parse(text);
} catch (err) {
console.error("Error parsing pix buffer:", err);
return null;
}
}

static async toSharp(pixData) {
const payload = pixData?.data ?? pixData;
const { width, height, channel, depth, hex_data } = payload;
if (!width || !height || !hex_data) return null;

const channels = channel ?? 4;
const raw = Buffer.from(hex_data, "hex");
const bytesPerPixel = Math.ceil(depth / 8) * channels;
const expectedLength = width * height * bytesPerPixel;
if (raw.length < expectedLength) raw.fill(0, raw.length, expectedLength);

return sharp(raw, {
Comment on lines +42 to +44
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The buffer filling logic may not work correctly when raw.length < expectedLength. Using fill() on the existing buffer doesn't extend it. Consider using Buffer.concat([raw, Buffer.alloc(expectedLength - raw.length)]) to properly pad the buffer to the expected length.

Suggested change
if (raw.length < expectedLength) raw.fill(0, raw.length, expectedLength);
return sharp(raw, {
const paddedRaw = raw.length < expectedLength
? Buffer.concat([raw, Buffer.alloc(expectedLength - raw.length)])
: raw;
return sharp(paddedRaw, {

Copilot uses AI. Check for mistakes.
raw: { width, height, channels, depth: depth ?? 8 },
})
.png()
.toBuffer({ resolveWithObject: true });
}

static async toPix(buffer, info) {
const { data, info: rawInfo } = await sharp(buffer)
Comment on lines +51 to +52
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused variable rawInfo.

Suggested change
static async toPix(buffer, info) {
const { data, info: rawInfo } = await sharp(buffer)
static async toPix(buffer) {
const { data, info } = await sharp(buffer)

Copilot uses AI. Check for mistakes.
.raw()
.toBuffer({ resolveWithObject: true });

return {
width: info.width,
height: info.height,
channel: info.channels,
depth: info.bitsPerSample || 8,
compression: {
method: "none",
},
hex_data: data.toString("hex"),
label: {},
joint: {},
};
}
}

module.exports = {
Pix,
};
27 changes: 22 additions & 5 deletions imgkit/features/image_layer_events.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,9 @@ class ImageLayerEvents {
y
);

const color_name = await ImageProcessor.getColorName(color);

navigator.clipboard.writeText(color);
// Send color to main renderer
ipcRenderer.send("colorpickerValueSEND", color, color_name);
ipcRenderer.send("colorpickerValueSEND", color);
}
});

Expand Down Expand Up @@ -101,6 +100,7 @@ class ImageLayerEvents {
const color = colorDiv.title;
if (color && color !== "empty") {
navigator.clipboard.writeText(color).then(() => {
ipcRenderer.send("colorpickerValueSEND", color);
ipcRenderer.send("showNotificationREQ", "imgkit-color-copy");
});
}
Expand Down Expand Up @@ -224,16 +224,33 @@ class ImageLayerEvents {
* Setup drag and drop handlers
*/
setupDragDrop() {
// Prevent default browser behavior
this.layer.canvas.addEventListener("dragover", (e) => e.preventDefault());
// Prevent default browser behavior and add rotating border effect
this.layer.canvas.addEventListener("dragover", (e) => {
e.preventDefault();
// Add rotating border animation class
this.layer.panel.classList.add("drag-border-active");
});

this.layer.canvas.addEventListener("dragenter", (e) => {
e.preventDefault();
this.sendLayerEvent("select");
// Add rotating border animation class
this.layer.panel.classList.add("drag-border-active");
});

this.layer.canvas.addEventListener("dragleave", (e) => {
// Only remove class if drag actually left the canvas (not moving to child element)
// Also handle case where relatedTarget is null (e.g., drag left browser window)
if (!e.relatedTarget || !this.layer.canvas.contains(e.relatedTarget)) {
this.layer.panel.classList.remove("drag-border-active");
}
});

this.layer.canvas.addEventListener("drop", async (e) => {
e.preventDefault();
// Remove rotating border animation class
this.layer.panel.classList.remove("drag-border-active");

const files = e.dataTransfer.files;

if (!files || files.length === 0) return;
Expand Down
31 changes: 31 additions & 0 deletions imgkit/imgpanel.css
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,37 @@
border-top: 3px solid #4caf50;
}

/* Rotating dotted border animation during drag-over */
@keyframes rotating-border {
from {
background-position: 0 0, 100% 100%, 0 100%, 100% 0;
}
to {
background-position: 12px 0, calc(100% - 12px) 100%, 0 calc(100% - 12px),
100% 12px;
}
}

.imgPanel.drag-border-active::before {
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border-radius: inherit;
pointer-events: none;
z-index: 10;
background-image: linear-gradient(90deg, #666 50%, transparent 50%),
linear-gradient(90deg, #666 50%, transparent 50%),
linear-gradient(0deg, #666 50%, transparent 50%),
linear-gradient(0deg, #666 50%, transparent 50%);
background-size: 12px 3px, 12px 3px, 3px 12px, 3px 12px;
background-position: 0 0, 100% 100%, 0 100%, 100% 0;
background-repeat: repeat-x, repeat-x, repeat-y, repeat-y;
animation: rotating-border 0.5s linear infinite;
}

.imgPanel:hover {
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
}
Expand Down
3 changes: 3 additions & 0 deletions imgkit/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ <h2 class="coord-text">( - , - )</h2>
<h2 class="info-text">0 x 0</h2>
</div>
<select class="extension-combo">
<option value="pix">pix</option>
<option value="png">png</option>
<option value="svg">svg</option>
<option value="jpg">jpg</option>
Expand All @@ -74,6 +75,8 @@ <h2 class="info-text">0 x 0</h2>
<option value="tif">tif</option>
<option value="tiff">tiff</option>
<option value="avif">avif</option>
<option value="heif">heif</option>
<option value="heic">heic</option>
</select>
</div>
</template>
Expand Down
Loading