depixel 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +2 -1
  2. package/lib.js +26 -25
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -7,9 +7,10 @@ Based on the following paper:
7
7
  And the [source code](https://github.com/falichs/Depixelizing-Pixel-Art-on-GPUs) included with this paper:
8
8
  * [Depixelizing Pixel Art on GPUs](https://www.cg.tuwien.ac.at/research/publications/2014/KREUZER-2014-DPA/) by Felix Kreuzer
9
9
 
10
- Improvements upon original
10
+ Improvements upon original GPU version:
11
11
  * Add a threshold parameter to adjust how close two colors need to be to be consider similar
12
12
  * Better handle alpha channels (treat as dissimilar from non-transparent pixels)
13
+ * Fix stretching/scaling due to texel misalignment
13
14
 
14
15
  Notes
15
16
  * Original GPU code is MIT licensed, the same license may apply here, all original code in this project is additionally released under the MIT License
package/lib.js CHANGED
@@ -80,6 +80,16 @@ function fetchPixelRGBA(src, x, y) {
80
80
  return [d[idx] / 255, d[idx + 1] / 255, d[idx + 2] / 255, d[idx + 3] / 255];
81
81
  }
82
82
 
83
+ function fetchPixelRGBA8(src, x, y) {
84
+ const w = src.width;
85
+ const h = src.height;
86
+ const cx = clampInt(x, 0, w - 1);
87
+ const cy = clampInt(y, 0, h - 1);
88
+ const idx = pixelIndex(cx, cy, w);
89
+ const d = src.data;
90
+ return [d[idx], d[idx + 1], d[idx + 2], d[idx + 3]];
91
+ }
92
+
83
93
  function isSimilar(a, b, threshold) {
84
94
  const yA = 0.299 * a[0] + 0.587 * a[1] + 0.114 * a[2];
85
95
  const uA = 0.493 * (a[2] - yA);
@@ -1428,8 +1438,8 @@ function gaussRasterize(src, sim, cell, positions, outW, outH) {
1428
1438
  for (let ox = 0; ox < outW; ox++) {
1429
1439
  let influencingPixels = [true, true, true, true];
1430
1440
  const cellSpaceCoords = [
1431
- (w - 1) * (ox / (outW - 1)),
1432
- (h - 1) * (oy / (outH - 1)),
1441
+ (w * (ox + 0.5) / outW) - 0.5 + 0.00001,
1442
+ (h * (oy + 0.5) / outH) - 0.5 + 0.00001,
1433
1443
  ];
1434
1444
  const fragmentBaseKnotIndex = (2 * Math.floor(cellSpaceCoords[0]) + Math.floor(cellSpaceCoords[1]) * 2 * (w - 1)) | 0;
1435
1445
  const node0flags = cell.flags[fragmentBaseKnotIndex] | 0;
@@ -1768,10 +1778,13 @@ function gaussRasterize(src, sim, cell, positions, outW, outH) {
1768
1778
 
1769
1779
  const outIdx = (oy * outW + ox) * 4;
1770
1780
  if (weightSum === 0) {
1771
- out[outIdx] = 0;
1772
- out[outIdx + 1] = 0;
1773
- out[outIdx + 2] = 0;
1774
- out[outIdx + 3] = 255;
1781
+ const nx = Math.round(cellSpaceCoords[0]);
1782
+ const ny = Math.round(cellSpaceCoords[1]);
1783
+ const col = fetchPixelRGBA8(src, nx, ny);
1784
+ out[outIdx] = col[0];
1785
+ out[outIdx + 1] = col[1];
1786
+ out[outIdx + 2] = col[2];
1787
+ out[outIdx + 3] = col[3];
1775
1788
  } else {
1776
1789
  out[outIdx] = clampInt(Math.round((colorSum[0] / weightSum) * 255), 0, 255);
1777
1790
  out[outIdx + 1] = clampInt(Math.round((colorSum[1] / weightSum) * 255), 0, 255);
@@ -1820,30 +1833,18 @@ function scaleImage(src, opts) {
1820
1833
 
1821
1834
  const borderPx = Math.max(0, Math.round(opts.borderPx || 0));
1822
1835
  if (borderPx > 0) {
1823
- const doInvisBorder = Math.min(
1824
- src.data[3],
1825
- src.data[(inW - 1) * 4 + 3],
1826
- src.data[(inH - 1) * inW * 4 + 3],
1827
- src.data[((inH - 1) * inW + (inW - 1)) * 4 + 3],
1828
- ) < 255;
1829
1836
  const padW = inW + 2 * borderPx;
1830
1837
  const padH = inH + 2 * borderPx;
1831
1838
  const padData = new Uint8Array(padW * padH * 4);
1832
- // fill magenta border (invisible if original image appears to have invisible edges)
1839
+ // copy src into center, expand into borders
1833
1840
  for (let y = 0; y < padH; y++) {
1834
- for (let x = 0; x < padW; x++) {
1835
- const idx = (y * padW + x) * 4;
1836
- padData[idx] = doInvisBorder ? 0 : 255;
1837
- padData[idx + 1] = 0;
1838
- padData[idx + 2] = doInvisBorder ? 0 : 255;
1839
- padData[idx + 3] = doInvisBorder ? 0 : 255;
1840
- }
1841
- }
1842
- // copy src into center
1843
- for (let y = 0; y < inH; y++) {
1844
- const srcRow = y * inW * 4;
1845
- const dstRow = (y + borderPx) * padW * 4 + borderPx * 4;
1841
+ const srcRow = Math.min(inH - 1, Math.max(0, y - borderPx)) * inW * 4;
1842
+ const dstRow = y * padW * 4 + borderPx * 4;
1846
1843
  padData.set(src.data.subarray(srcRow, srcRow + inW * 4), dstRow);
1844
+ for (let ii = 0; ii < borderPx * 4; ++ii) {
1845
+ padData[dstRow - borderPx * 4 + ii] = src.data[srcRow + ii % 4];
1846
+ padData[dstRow + inW * 4 + ii] = src.data[srcRow + ii % 4];
1847
+ }
1847
1848
  }
1848
1849
  const scale = outH / inH;
1849
1850
  const outHpad = Math.max(1, Math.round(padH * scale));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "depixel",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "Depixelizing Pixel Art by Kopf-Lischinski and Felix Kreuzer for Node.js",
5
5
  "main": "lib.js",
6
6
  "keywords": [