added image resizing to export, still needs UI and details...

Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
Alex A. Naanou 2020-10-30 18:13:53 +03:00
parent 43f0499843
commit a34591065a
3 changed files with 224 additions and 34 deletions

View File

@ -2082,8 +2082,10 @@ var FileSystemWriterActions = actions.Actions({
// XXX handle .image.path and other stack files...
// XXX local collections???
//
// XXX BUG: seems to ignore max_size...
// ...'preview-size' does not affect the base image size...
// XXX BUG: max_size is measured by preview size and ignores main
// image size...
// ...this results in exported images being previews ONLY IF
// they have previews larger than max_size...
// XXX BUG: this does not remove previews correctly...
// to reproduce:
// open: L:\media\img\my\2019
@ -2123,10 +2125,14 @@ var FileSystemWriterActions = actions.Actions({
path = path || './exported'
path = util.normalizePath(path)
max_size = parseInt(max_size || settings['preview-size-limit']) || null
max_size = parseInt(max_size
|| settings['preview-size-limit'])
|| null
// XXX make this dependant on max_size....
include_orig = include_orig || true
var resize = max_size && this.makeResizedImage
// clear/backup target...
clean_target_dir = clean_target_dir === undefined ?
settings['clean-target']
@ -2258,8 +2264,12 @@ var FileSystemWriterActions = actions.Actions({
// we are using as the primary image to
// save space...
: null })
// add primary image...
.concat(include_orig && img.path ?
// add primary image (copy)...
// XXX check if any of the previews/main images
// matches the size and copy instead of resize...
.concat((!resize
&& include_orig
&& img.path) ?
[[
(replace_orig && max != null) ?
// replace the base image with the
@ -2306,18 +2316,25 @@ var FileSystemWriterActions = actions.Actions({
// XXX do we queue these or let the OS handle it???
// ...needs testing, if node's fs queues the io
// internally then we do not need to bother...
queue.push(copy(from, to)
.then(function(){
logger && logger.emit('done', to) })
.catch(function(err){
logger && logger.emit('error', err) })) } }) } })
queue
.push(copy(from, to)
.then(function(){
logger && logger.emit('done', to) })
.catch(function(err){
logger && logger.emit('error', err) })) } }) } })
// prep the index...
// primary image (resize)...
resize
&& include_orig
&& queue
.push(this.makeResizedImage(gids, max_size, path, { logger }))
// index...
var index = this.prepareIndexForWrite(json, true)
// NOTE: if we are to use .saveIndex(..) here, do not forget
// to reset .changes
queue.push(
queue
.push(
file.writeIndex(
index.index,
index_path,
@ -2335,8 +2352,7 @@ var FileSystemWriterActions = actions.Actions({
return Promise.all(queue) }],
// XXX BUG: seems to ignore max_size...
// ...'preview-size' does not affect the base image size...
// XXX ASAP add option to control copy/resize -> .makeResizedImage(..)...
// XXX might also be good to save/load the export options to .ImageGrid-export.json
// XXX resolve env variables in path... (???)
// XXX make custom previews (option)...
@ -2458,8 +2474,14 @@ var FileSystemWriterActions = actions.Actions({
&& !fse.existsSync(to)
&& outputFile(to, img.text || '')
// normal images...
// normal images (resize)...
} else if(that.makeResizedImage){
// XXX can we make this batch...
return that.makeResizedImage(gid, size, img_dir, { name, logger })
// normal images (copy)...
} else {
//*/
// NOTE: we are intentionally losing image dir
// name here -- we do not need to preserve
// topology when exporting...
@ -2470,10 +2492,6 @@ var FileSystemWriterActions = actions.Actions({
+'/'
+ that.images.getBestPreview(gid, size).url
// XXX see if we need to make a preview (sharp)
// XXX
var to = img_dir +'/'+ name
logger && logger.emit('queued', to)

View File

@ -34,6 +34,7 @@ if(typeof(process) != 'undefined'){
/*********************************************************************/
if(typeof(process) != 'undefined'){
var copy = file.denodeify(fse.copy)
var ensureDir = file.denodeify(fse.ensureDir)
}
@ -112,15 +113,184 @@ var SharpActions = actions.Actions({
}
})
this.previewConstructorWorker.__post_handlers = {}
}],
this.previewConstructorWorker.__post_handlers = {} }],
stopPreviewWorker: ['- Sharp/',
function(){
this.previewConstructorWorker && this.previewConstructorWorker.kill()
delete this.previewConstructorWorker
}],
delete this.previewConstructorWorker }],
// XXX should this resize up??? ...option???
// XXX add transform/crop support...
// XXX revise logging...
makeResizedImage: ['- Image/',
core.doc`Make resized image(s)...
.makeResizedImage(gid, size, path[, options])
.makeResizedImage(gids, size, path[, options])
-> promise
Image size formats:
500px - resize to make image's *largest* dimension 500 pixels (default).
500p - resize to make image's *smallest* dimension 500 pixels.
500 - same as 500px
options format:
{
// output image name...
//
// Used if processing a single image, ignored otherwise.
name: <str>,
// image name pattern and data...
//
// NOTE: for more info on pattern see: .formatImageName(..)
pattern: <str>,
data: { .. },
// overwrite, backup or skip (default) existing images...
//
// default: null / false
overwrite: true | 'backup' | false,
// XXX not implemented...
transform: ...,
crop: ...,
logger: ...
, }
NOTE: this will not overwrite existing images.
`,
function(images, size, path, options={}){
var that = this
// sanity check...
if(arguments.length < 3){
throw new Error('.makeResizedImage(..): '
+'need at least images, size and path.') }
// get/normalize images...
//images = images || this.current
images = images
|| 'all'
// keywords...
images = images == 'all' ?
this.data.getImages('all')
: images == 'current' ?
this.current
: images
images = images instanceof Array ?
images
: [images]
// sizing...
var fit =
typeof(size) == typeof('str') ?
(size.endsWith('px') ?
'inside'
: size.endsWith('p') ?
'outside'
: 'inside')
: 'inside'
size = parseInt(size)
// options...
var {
// naming...
name,
pattern,
data,
// file handling...
overwrite,
// transformations...
// XXX not implemented...
transform,
// XXX not implemented...
crop,
logger,
} = options
// defaults...
pattern = pattern || '%n'
/* XXX
transform = transform === undefined ?
true
: transform
//*/
logger = logger || this.logger
logger = logger && logger.push('Resize')
var timestamp = Date.timeStamp()
return Promise.all(images
.map(function(gid){
// skip non-images...
if(that.images[gid].type != undefined){
return }
// paths...
var source = that.getImagePath(gid)
var to = pathlib.join(
path,
(images.length == 1 && name) ?
name
: that.formatImageName(pattern, gid, data || {}))
logger && logger.emit('queued', to)
// existing image...
if(fse.existsSync(to)){
// rename...
if(overwrite == 'backup'){
var i = 0
while(fse.existsSync(`${to}.${timestamp}'-bak`+ (i || ''))){
i++ }
fse.renameSync(
to,
fse.existsSync(`${to}.${timestamp}'-bak`+ (i || '')))
// remove...
} else if(overwrite){
fse.removeSync(to)
// skip...
} else {
logger && logger.emit('skipping', to)
return } }
return ensureDir(pathlib.dirname(to))
.then(function(){
return sharp(source)
.clone()
// handle transform (.orientation / .flip) and .crop...
.run(function(){
// XXX
if(transform || crop){
throw new Error('.makeResizedImage(..): '
+[
transform ? 'transform' : [],
crop ? 'crop' : [],
].flat().join(' and ')
+' not implemented...') }
// XXX need clear spec defining what
// order transforms are applied
// and in which coordinates we
// crop (i.e. pre/post transform)...
if(transform){
}
if(crop){
}
})
.resize({
width: size,
height: size,
fit: fit,
})
.withMetadata()
.toFile(to)
.then(function(){
logger
&& logger.emit('done', to) })}) })) }],
// XXX use .makeResizedImage(..)
// XXX should this account for non-jpeg images???
// XXX BUG?: this breaks on PNG images...
// XXX log: count gids and not specific images...
@ -150,12 +320,17 @@ var SharpActions = actions.Actions({
// get/normalize images...
//images = images || this.current
images = images || 'all'
images = images
|| 'all'
// keywords...
images = images == 'all' ? this.data.getImages('all')
: images == 'current' ? this.current
images = images == 'all' ?
this.data.getImages('all')
: images == 'current' ?
this.current
: images
images = images instanceof Array ? images : [images]
images = images instanceof Array ?
images
: [images]
//
// Format:
@ -203,8 +378,7 @@ var SharpActions = actions.Actions({
.unique()
} else {
sizes = cfg_sizes
}
sizes = cfg_sizes }
var path_tpl = that.config['preview-path-template']
.replace(/\$INDEX|\$\{INDEX\}/g, that.config['index-dir'] || '.ImageGrid')
@ -280,8 +454,7 @@ var SharpActions = actions.Actions({
sizes,
base_path,
path_tpl,
post_handler) }))}
}],
post_handler) }))} }],
})

View File

@ -80,8 +80,7 @@ function(images, sizes, base_path, target_tpl, callback){
.map(function(res){
// skip if image is smaller than res...
if(res >= orig_res){
return
}
return }
var rel = target
.replace(/\$RESOLUTION|\$\{RESOLUTION\}/g, res)