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

View File

@ -34,6 +34,7 @@ if(typeof(process) != 'undefined'){
/*********************************************************************/ /*********************************************************************/
if(typeof(process) != 'undefined'){ if(typeof(process) != 'undefined'){
var copy = file.denodeify(fse.copy)
var ensureDir = file.denodeify(fse.ensureDir) var ensureDir = file.denodeify(fse.ensureDir)
} }
@ -112,15 +113,184 @@ var SharpActions = actions.Actions({
} }
}) })
this.previewConstructorWorker.__post_handlers = {} this.previewConstructorWorker.__post_handlers = {} }],
}],
stopPreviewWorker: ['- Sharp/', stopPreviewWorker: ['- Sharp/',
function(){ function(){
this.previewConstructorWorker && this.previewConstructorWorker.kill() 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 should this account for non-jpeg images???
// XXX BUG?: this breaks on PNG images... // XXX BUG?: this breaks on PNG images...
// XXX log: count gids and not specific images... // XXX log: count gids and not specific images...
@ -150,12 +320,17 @@ var SharpActions = actions.Actions({
// get/normalize images... // get/normalize images...
//images = images || this.current //images = images || this.current
images = images || 'all' images = images
|| 'all'
// keywords... // keywords...
images = images == 'all' ? this.data.getImages('all') images = images == 'all' ?
: images == 'current' ? this.current this.data.getImages('all')
: images == 'current' ?
this.current
: images : images
images = images instanceof Array ? images : [images] images = images instanceof Array ?
images
: [images]
// //
// Format: // Format:
@ -203,8 +378,7 @@ var SharpActions = actions.Actions({
.unique() .unique()
} else { } else {
sizes = cfg_sizes sizes = cfg_sizes }
}
var path_tpl = that.config['preview-path-template'] var path_tpl = that.config['preview-path-template']
.replace(/\$INDEX|\$\{INDEX\}/g, that.config['index-dir'] || '.ImageGrid') .replace(/\$INDEX|\$\{INDEX\}/g, that.config['index-dir'] || '.ImageGrid')
@ -280,8 +454,7 @@ var SharpActions = actions.Actions({
sizes, sizes,
base_path, base_path,
path_tpl, path_tpl,
post_handler) }))} post_handler) }))} }],
}],
}) })

View File

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