Skip to content

Commit

Permalink
Implement command line interface
Browse files Browse the repository at this point in the history
Merge pull request #9 from haroldo-ok/command-line

Implemented command line interface; now, the application can be called either from command line, from browser or as a node.js module.

Help message:
```
rgbquant-sms convert <src> <dest>

Converts an image into a png with the tile count reduced

Positionals:
  src   The source image, the one that will be converted     [string] [required]
  dest  The destination image that will be generated         [string] [required]

Options:
  --version           Show version number                              [boolean]
  --help              Show help                                        [boolean]
  --colors            Desired palette size                         [default: 16]
  --max-tiles         Maximum number of tiles to use              [default: 256]
  --min-hue-cols      Number of colors per hue group to evaluate regardless of
                      counts, to retain low-count hues            [default: 512]
  --dithKern          Dithering kernel name; can one of these:
                      FloydSteinberg, Stucki, Atkinson, Jarvis, Burkes, Sierra,
                      TwoSierra, SierraLite, FalseFloydSteinberg, Ordered2,
                      Ordered2x1, Ordered3, Ordered4 or Ordered8
                                                          [string] [default: ""]
  --dith-serp         Enable serpentine pattern dithering
                                                      [boolean] [default: false]
  --weigh-popularity  Weigh by popularity when reducing tile count
                                                       [boolean] [default: true]
  --weigh-entropy     Weigh by entropy when reducing tile count
                                                      [boolean] [default: false]
```
  • Loading branch information
haroldo-ok authored Aug 9, 2023
2 parents f7fd1e4 + 83d3bd0 commit eb056b2
Show file tree
Hide file tree
Showing 5 changed files with 349 additions and 29 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/node_modules
/*.png
145 changes: 121 additions & 24 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,52 @@
'use strict';

const fs = require('fs');
const _ = require('underscore');

const Canvas = require('canvas');
const Image = Canvas.Image;

const { RgbQuantSMS } = require('./src/rgbquant-sms');

console.log('Canvas', Canvas);
// From `helpers.js`
function drawPixels(idxi8, width0, width1) {
var idxi32 = new Uint32Array(idxi8.buffer);

(async () => {

const quant = new RgbQuantSMS({
colors: 16,
paletteCount: 1,
maxTiles: 256,
minHueCols: 0,
weighPopularity: true
});
width1 = width1 || width0;

const canvasWidth = width0;
const canvasHeight = Math.ceil(idxi32.length / width0);

var can = new Canvas.Canvas(canvasWidth, canvasHeight),
can2 = new Canvas.Canvas(canvasWidth, canvasHeight),
ctx = can.getContext("2d"),
ctx2 = can2.getContext("2d");

ctx.imageSmoothingEnabled = ctx.mozImageSmoothingEnabled = ctx.webkitImageSmoothingEnabled = ctx.msImageSmoothingEnabled = false;
ctx2.imageSmoothingEnabled = ctx2.mozImageSmoothingEnabled = ctx2.webkitImageSmoothingEnabled = ctx2.msImageSmoothingEnabled = false;

var imgd = ctx.createImageData(can.width, can.height);

var buf32 = new Uint32Array(imgd.data.buffer);
buf32.set(idxi32);

ctx.putImageData(imgd, 0, 0);

ctx2.drawImage(can, 0, 0, can2.width, can2.height);

return can2;
}

// From `demo.js`
const tileMapToCanvas = tileMap => {
var image = new RgbQuantSMS.IndexedImage(tileMap.mapW * 8, tileMap.mapH * 8, tileMap.palettes);
image.drawMap(tileMap);
var ican = drawPixels(image.toRgbBytes(), image.width);
return ican;
}

const convert = async (src, dest, options) => {
const quant = new RgbQuantSMS(options);

const getCanvas = async (src) => new Promise((resolve, reject) => {
const img = new Image();
Expand All @@ -29,19 +60,85 @@ console.log('Canvas', Canvas);
img.onerror = reject;
img.src = src;
});

const canvas = await getCanvas('demo/img/biking.jpg');
console.log('canvas', canvas);

quant.sample(canvas);
const palettes = quant.palettes();
console.log('palettes', palettes)

const unoptimizedTileMap = quant.reduceToTileMap(canvas);
const optimizedTileMap = quant.normalizeTiles(unoptimizedTileMap);
quant.updateTileEntropy(optimizedTileMap.tiles);
const similarTiles = quant.groupBySimilarity(optimizedTileMap);
const reducedTileMap = quant.removeSimilarTiles(optimizedTileMap, similarTiles);
const saveToFile = (fileName, tileMap) => {
const outCanvas = tileMapToCanvas(tileMap);
const buffer = outCanvas.toBuffer('image/png');
fs.writeFileSync(fileName, buffer);
}

const canvas = await getCanvas(src);

console.log('reducedTileMap', { reducedTileMap, tileCount: reducedTileMap.tiles.length });
})();
const reducedTileMap = quant.convert(canvas);

saveToFile(dest, reducedTileMap);
}

/* if called directly from command line or from a shell script */
if (require.main === module) {
const yargs = require('yargs');

const commandLine = yargs.scriptName('rgbquant-sms')
.usage('$0 <cmd> [args]')
.command('convert <src> <dest>', 'Converts an image into a png with the tile count reduced', (yargs) => {
yargs
.positional('src', {
type: 'string',
describe: 'The source image, the one that will be converted'
})
.positional('dest', {
type: 'string',
describe: 'The destination image that will be generated'
})
.options({
'colors': {
default: 16,
describe: 'Desired palette size',
type: 'integer'
},
'max-tiles': {
default: 256,
describe: 'Maximum number of tiles to use',
type: 'integer'
},
'min-hue-cols': {
default: 512,
describe: 'Number of colors per hue group to evaluate regardless of counts, to retain low-count hues',
type: 'integer'
},
'dithKern': {
default: '',
describe: 'Dithering kernel name; can one of these:\n' +
'FloydSteinberg, Stucki, Atkinson, Jarvis, Burkes, Sierra, TwoSierra, SierraLite, ' +
'FalseFloydSteinberg, Ordered2, Ordered2x1, Ordered3, Ordered4 or Ordered8',
type: 'string'
},
'dith-serp': {
default: false,
describe: 'Enable serpentine pattern dithering',
type: 'boolean'
},
'weigh-popularity': {
default: true,
describe: 'Weigh by popularity when reducing tile count',
type: 'boolean'
},
'weigh-entropy': {
default: false,
describe: 'Weigh by entropy when reducing tile count',
type: 'boolean'
}
});
})
.demandCommand(1, 'You need to inform at least one command before moving on')
.strict()
.help()
.argv;

if (commandLine._.includes('convert')) {
const options = _.pick(commandLine, 'colors', 'maxTiles', 'minHueCols', 'dithKern', 'dithSerp', 'weighPopularity', 'weighEntropy');
convert(commandLine.src, commandLine.dest, options);
}
}

module.exports = { RgbQuantSMS, convert };
Loading

0 comments on commit eb056b2

Please sign in to comment.