greetings all
|
@ -0,0 +1,5 @@
|
||||||
|
.idea
|
||||||
|
image/dzi
|
||||||
|
image/original
|
||||||
|
image/script
|
||||||
|
image/image.txt
|
|
@ -0,0 +1,72 @@
|
||||||
|
[{
|
||||||
|
"@context": "http://www.w3.org/ns/anno.jsonld",
|
||||||
|
"id": "#a88b22d0-6106-4872-9435-c78b5e890000",
|
||||||
|
"type": "Annotation",
|
||||||
|
"body": [{
|
||||||
|
"type": "TextualBody",
|
||||||
|
"value": "<a href = 'https://sketchersunited.org/posts/112'>ruler</a> by <a href = 'https://sketchersunited.org/users/9'>@username</a>",
|
||||||
|
"format" : "text/html",
|
||||||
|
"language" : "en"
|
||||||
|
}],
|
||||||
|
"target": {
|
||||||
|
"selector": {
|
||||||
|
"type": "FragmentSelector",
|
||||||
|
"conformsTo": "http://www.w3.org/TR/media-frags/",
|
||||||
|
"value": "xywh=pixel:0,0,1000,1000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@context": "http://www.w3.org/ns/anno.jsonld",
|
||||||
|
"id": "#a88b22d0-6106-4872-9435-c78b5e890001",
|
||||||
|
"type": "Annotation",
|
||||||
|
"body": [{
|
||||||
|
"type": "TextualBody",
|
||||||
|
"value": "<a href = 'https://sketchersunited.org/posts/112'>screwdriver</a> by <a href = 'https://sketchersunited.org/users/9'>@username</a>",
|
||||||
|
"format" : "text/html",
|
||||||
|
"language" : "en"
|
||||||
|
}],
|
||||||
|
"target": {
|
||||||
|
"selector": {
|
||||||
|
"type": "FragmentSelector",
|
||||||
|
"conformsTo": "http://www.w3.org/TR/media-frags/",
|
||||||
|
"value": "xywh=pixel:0,1000,1000,1000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@context": "http://www.w3.org/ns/anno.jsonld",
|
||||||
|
"id": "#a88b22d0-6106-4872-9435-c78b5e890002",
|
||||||
|
"type": "Annotation",
|
||||||
|
"body": [{
|
||||||
|
"type": "TextualBody",
|
||||||
|
"value": "<a href = 'https://sketchersunited.org/posts/112'>hammer</a> by <a href = 'https://sketchersunited.org/users/9'>@username</a>",
|
||||||
|
"format" : "text/html",
|
||||||
|
"language" : "en"
|
||||||
|
}],
|
||||||
|
"target": {
|
||||||
|
"selector": {
|
||||||
|
"type": "FragmentSelector",
|
||||||
|
"conformsTo": "http://www.w3.org/TR/media-frags/",
|
||||||
|
"value": "xywh=pixel:1000,0,1000,1000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@context": "http://www.w3.org/ns/anno.jsonld",
|
||||||
|
"id": "#a88b22d0-6106-4872-9435-c78b5e890003",
|
||||||
|
"type": "Annotation",
|
||||||
|
"body": [{
|
||||||
|
"type": "TextualBody",
|
||||||
|
"value": "<a href = 'https://sketchersunited.org/posts/112'>drill</a> by <a href = 'https://sketchersunited.org/users/9'>@username</a>",
|
||||||
|
"format" : "text/html",
|
||||||
|
"language" : "en"
|
||||||
|
}],
|
||||||
|
"target": {
|
||||||
|
"selector": {
|
||||||
|
"type": "FragmentSelector",
|
||||||
|
"conformsTo": "http://www.w3.org/TR/media-frags/",
|
||||||
|
"value": "xywh=pixel:1000,1000,1000,1000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]
|
|
@ -0,0 +1,18 @@
|
||||||
|
[{
|
||||||
|
"@context": "http://www.w3.org/ns/anno.jsonld",
|
||||||
|
"id": "#a88b22d0-6106-4872-9435-c78b5e890000",
|
||||||
|
"type": "Annotation",
|
||||||
|
"body": [{
|
||||||
|
"type": "TextualBody",
|
||||||
|
"value": "Nothing here yet!",
|
||||||
|
"format" : "text/html",
|
||||||
|
"language" : "en"
|
||||||
|
}],
|
||||||
|
"target": {
|
||||||
|
"selector": {
|
||||||
|
"type": "FragmentSelector",
|
||||||
|
"conformsTo": "http://www.w3.org/TR/media-frags/",
|
||||||
|
"value": "xywh=pixel:0,0,1000,1000"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]
|
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 803 B |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 15 KiB |
|
@ -0,0 +1 @@
|
||||||
|
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
|
After Width: | Height: | Size: 575 KiB |
|
@ -0,0 +1,958 @@
|
||||||
|
#!/bin/bash
|
||||||
|
version="0.004"
|
||||||
|
date="05/08/2015"
|
||||||
|
|
||||||
|
# ####### Options ####### #
|
||||||
|
# resultExt='png'
|
||||||
|
resultExt=''
|
||||||
|
resizeFilter='' # http://www.imagemagick.org/Usage/filter/
|
||||||
|
# resultDir='./sliceResult'
|
||||||
|
resultDir=''
|
||||||
|
|
||||||
|
# Selector fo slicer: A or B
|
||||||
|
scaleFromImage=true # Type of scaling: if true - scale calculates from image size to down (slicer A), if false - image scale starts from tile size and grow up (slicer B)
|
||||||
|
gravity='NorthWest' # Image positioning (from this option depends, which tiles sides can be cropped, if it not full size). Choices include: 'NorthWest', 'North', 'NorthEast', 'West', 'Center', 'East', 'SouthWest', 'South', 'SouthEast'. Use -list gravity to get a complete list of -gravity settings available in your ImageMagick installation.
|
||||||
|
extent=false # Extent option (false - tiles be cropped, true - will be added transparent color to get all tiles of full size)
|
||||||
|
scale64=false
|
||||||
|
zoomReverse=false # false: maxZoom=100%; true: minZoom=100%
|
||||||
|
|
||||||
|
# Options only for slicerB
|
||||||
|
scaleover=true # Maximum zoom: bigger or less then image. False - will not create upscaled image for maximum zoom; true - last zoom be equal or grater, then image.
|
||||||
|
horizontal=true # Type of positioning of image: horizontal or vertical.
|
||||||
|
|
||||||
|
# ####### Options end ####### #
|
||||||
|
|
||||||
|
# ———————————————————————————————————————————————————————————————————————————————————
|
||||||
|
# ####### Variables ####### #
|
||||||
|
imageSource=''
|
||||||
|
tileW=256
|
||||||
|
tileH=256
|
||||||
|
step=200
|
||||||
|
imageW=''
|
||||||
|
imageH=''
|
||||||
|
imOptions=''
|
||||||
|
dziFormat=true
|
||||||
|
verboseLevel=0
|
||||||
|
overlap=0 # TODO: Overlap handling.
|
||||||
|
# ———————————————————————————————————————————————————————————————————————————————————
|
||||||
|
# ####### Verbose functions ####### #
|
||||||
|
# (0=none, 1=warnings, 2=warnings+info, 3=warning+info+debug)
|
||||||
|
|
||||||
|
warnMsg(){
|
||||||
|
if [ $verboseLevel -ge 1 ]
|
||||||
|
then
|
||||||
|
echo $1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
infoMsg(){
|
||||||
|
if [ $verboseLevel -ge 2 ]
|
||||||
|
then
|
||||||
|
echo $1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
debugMsg(){
|
||||||
|
if [ $verboseLevel -ge 3 ]
|
||||||
|
then
|
||||||
|
echo $1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ———————————————————————————————————————————————————————————————————————————————————
|
||||||
|
# ####### CLI ####### #
|
||||||
|
|
||||||
|
qHelp(){
|
||||||
|
echo " -?, --help [option]"
|
||||||
|
if [ "$1" = true ]
|
||||||
|
then
|
||||||
|
echo " Show basic help or show help for selected option."
|
||||||
|
echo
|
||||||
|
echo " Type: str"
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
mHelp(){
|
||||||
|
echo " -m, --man"
|
||||||
|
if [ "$1" = true ]
|
||||||
|
then
|
||||||
|
echo " Show full help for all options."
|
||||||
|
echo
|
||||||
|
echo " Type: str"
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
iHelp(){
|
||||||
|
echo " [ -i, --in <file_path> ]"
|
||||||
|
if [ "$1" = true ]
|
||||||
|
then
|
||||||
|
echo " Input file to slice."
|
||||||
|
echo
|
||||||
|
echo " Type: str"
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
oHelp(){
|
||||||
|
echo " [ -o, --out <directory_path> ]"
|
||||||
|
if [ "$1" = true ]
|
||||||
|
then
|
||||||
|
echo " Output directory for result."
|
||||||
|
echo
|
||||||
|
echo " Default: same as source"
|
||||||
|
echo " Type: str"
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
# sliceResult/<zoom_Level>/<horizontal tiles line (x) (folder)>/<vertical tiles line (y) (file)>"
|
||||||
|
}
|
||||||
|
|
||||||
|
eHelp(){
|
||||||
|
echo " [ -e, --extension <file_extesion> ]"
|
||||||
|
if [ "$1" = true ]
|
||||||
|
then
|
||||||
|
echo " Set result files extension."
|
||||||
|
echo
|
||||||
|
echo " Default: same as source"
|
||||||
|
echo " Type: str"
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
wHelp(){
|
||||||
|
echo " [ -w, --width <tile_width> ]"
|
||||||
|
if [ "$1" = true ]
|
||||||
|
then
|
||||||
|
echo " Set tile width."
|
||||||
|
echo
|
||||||
|
echo " Default: 256 pixels or same as height, if height is present."
|
||||||
|
echo " Type: int"
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
hHelp(){
|
||||||
|
echo " [ -h, --height <tile_height> ]"
|
||||||
|
if [ "$1" = true ]
|
||||||
|
then
|
||||||
|
echo " Set tile height"
|
||||||
|
echo
|
||||||
|
echo " Default: 256 pixels or same as width, if width is present."
|
||||||
|
echo " Type: int"
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
sHelp(){
|
||||||
|
echo " [ -s, --step <zoom_step_value> ]"
|
||||||
|
if [ "$1" = true ]
|
||||||
|
then
|
||||||
|
echo " Zoom step value. Formula:"
|
||||||
|
echo " (1) image_size[i+1] = image_size[i] * 100 / step"
|
||||||
|
echo " 200 -> 200% or 2x -> 100% * 100 / 200 = 50%"
|
||||||
|
echo " 175 -> 175% or 1.75x -> 100% * 100 / 175 = 57.(142857)%"
|
||||||
|
echo " 120 -> 120% or 1.2x -> 100% * 100 / 120 = 83.(3)%"
|
||||||
|
echo " 300 -> 300% or 3x -> 100% * 100 / 300 = 33.(3)%"
|
||||||
|
echo " 100 -> 100% or 1x (no resize) -> infinity loop. Don't use it."
|
||||||
|
echo
|
||||||
|
echo " Default: 200"
|
||||||
|
echo " Type: int"
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
pHelp(){
|
||||||
|
echo " [ -p, --options 'imagemagick options string']"
|
||||||
|
if [ "$1" = true ]
|
||||||
|
then
|
||||||
|
echo " Specifies additional imagemagick options for 'convert'."
|
||||||
|
echo
|
||||||
|
echo " Type: str"
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
gHelp(){
|
||||||
|
echo " [ -g, --gravity <type> ]"
|
||||||
|
if [ "$1" = true ]
|
||||||
|
then
|
||||||
|
echo " Types: NorthWest North NorthEast West Center East SouthWest South SouthEast"
|
||||||
|
echo " The direction you choose specifies where to position of cuts start on image. Use -list gravity to get a complete list of -gravity settings available in your ImageMagick installation."
|
||||||
|
echo " http://www.imagemagick.org/script/command-line-options.php#gravity"
|
||||||
|
echo
|
||||||
|
echo " Default: NorthWest"
|
||||||
|
echo " Type: str"
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
xHelp(){
|
||||||
|
echo " [ -x, --extent ]"
|
||||||
|
if [ "$1" = true ]
|
||||||
|
then
|
||||||
|
echo " Specifies the edge tiles size: cropped or extent them to full size and fill transparent color."
|
||||||
|
echo
|
||||||
|
echo " Default: false"
|
||||||
|
echo " Type: logic switch"
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
dHelp(){
|
||||||
|
echo " [ -d, --dzi ]"
|
||||||
|
if [ "$1" = true ]
|
||||||
|
then
|
||||||
|
echo " Specifies output format."
|
||||||
|
echo
|
||||||
|
echo " Default: true"
|
||||||
|
echo " Type: logic switch"
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
aHelp(){
|
||||||
|
echo " [ -a, --slicea ]"
|
||||||
|
if [ "$1" = true ]
|
||||||
|
then
|
||||||
|
echo " Type of slicing - slice A. Image scale starts from image size to down. Inverts option '--sliceb'."
|
||||||
|
echo
|
||||||
|
echo " Default: true"
|
||||||
|
echo " Type: logic switch"
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
bHelp(){
|
||||||
|
echo " [ -b, --sliceb ]"
|
||||||
|
if [ "$1" = true ]
|
||||||
|
then
|
||||||
|
echo " Type of slicing - slice B. Image scale starts from tile size and grow up. Inverts option '--slicea'."
|
||||||
|
echo
|
||||||
|
echo " Default: false"
|
||||||
|
echo " Type: logic switch"
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
cHelp(){
|
||||||
|
echo " [ -c, --scaleover ] "
|
||||||
|
if [ "$1" = true ]
|
||||||
|
then
|
||||||
|
echo " Create upscaled image for maximum zoom (last zoom be equal or grater, then image). zoom[i-1]_size < source_image_size < zoom[i]_size"
|
||||||
|
echo " Work only in slice B mode. In other cases ignored."
|
||||||
|
echo
|
||||||
|
echo " Default: false"
|
||||||
|
echo " Type: logic switch"
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
rHelp(){
|
||||||
|
echo " [ -r, --horizontal ] "
|
||||||
|
if [ "$1" = true ]
|
||||||
|
then
|
||||||
|
echo " Tiles divide image on horizontal side without remains. On this side tiles will not be croped."
|
||||||
|
echo " Work only in slice B mode. In other cases ignored."
|
||||||
|
echo
|
||||||
|
echo " ___ ___ ___ _ _"
|
||||||
|
echo " | | | | ^"
|
||||||
|
echo " |___|___|___| Image"
|
||||||
|
echo " |___|___|___| _v_ <- Not full tiles."
|
||||||
|
echo " |_._|_._|_._| <-- Transparent color (extent=true) or cropped (extent=false)"
|
||||||
|
echo
|
||||||
|
echo " Default: true"
|
||||||
|
echo " Type: logic switch"
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
tHelp(){
|
||||||
|
echo " [ -t, --vertical ] "
|
||||||
|
if [ "$1" = true ]
|
||||||
|
then
|
||||||
|
echo " Tiles divide image on vertical side without remains. On this side tiles will not be croped."
|
||||||
|
echo " Work only in slice B mode. In other cases ignored."
|
||||||
|
echo
|
||||||
|
echo " |<-image->|"
|
||||||
|
echo " ___ ___ _ _ _ _"
|
||||||
|
echo " | | | |.| ^"
|
||||||
|
echo " |___|___|_|_| _v_Tile"
|
||||||
|
echo " | | | |.|"
|
||||||
|
echo " |___|___|_|_|"
|
||||||
|
echo " ^-- Transparent color (extent=true) or cropped (extent=false)"
|
||||||
|
echo " ^--- Not full tiles"
|
||||||
|
echo
|
||||||
|
echo " Default: false"
|
||||||
|
echo " Type: logic switch"
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
vHelp(){
|
||||||
|
echo " [ -v, --verbose <level> ]"
|
||||||
|
if [ "$1" = true ]
|
||||||
|
then
|
||||||
|
echo " User-selected verbosity levels:"
|
||||||
|
echo " - 0 = none"
|
||||||
|
echo " - 1 = warnings"
|
||||||
|
echo " - 2 = warnings + info"
|
||||||
|
echo " - 3 = warnings + info + debug"
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
echo " [ -v0, -v1, -v2, -v3 ]"
|
||||||
|
if [ "$1" = true ]
|
||||||
|
then
|
||||||
|
echo " Short commands for each verbosity level."
|
||||||
|
echo
|
||||||
|
echo " Default: 0"
|
||||||
|
echo " Type: logic switch"
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
lHelp(){
|
||||||
|
echo " [ -l, --overlap <pixels> ] "
|
||||||
|
if [ "$1" = true ]
|
||||||
|
then
|
||||||
|
echo " Tiles overlap in pixels."
|
||||||
|
echo
|
||||||
|
echo " Default: 1"
|
||||||
|
echo " Type: int"
|
||||||
|
echo
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
uHelp(){
|
||||||
|
echo " Usage:"
|
||||||
|
echo " magick-slicer.sh -u|--usage"
|
||||||
|
echo " magick-slicer.sh -?|--help|-m|--man [option_name]"
|
||||||
|
echo " magick-slicer.sh [options] [-i] /source/image [[-o] result/dir]"
|
||||||
|
echo " magick-slicer.sh /source/image [options] [result/dir]"
|
||||||
|
echo " magick-slicer.sh /source/image [result/dir] [options]"
|
||||||
|
echo
|
||||||
|
echo " Use quotes for path or options with spaces. First unknown string interpreting as source image, if it not defined. Second unknown string is interpreting as result path, if it not defined. Also, for source and result you can use options '-i' and '-o'."
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
cliHelp(){
|
||||||
|
echo " Map tiles generator. License: MIT."
|
||||||
|
echo " Version: $version"
|
||||||
|
echo " Date: $date"
|
||||||
|
echo
|
||||||
|
|
||||||
|
case $1 in
|
||||||
|
|
||||||
|
-i|--in) iHelp true ;;
|
||||||
|
-o|--out) oHelp true ;;
|
||||||
|
-e|--extension) eHelp true ;;
|
||||||
|
-w|--width) wHelp true ;;
|
||||||
|
-h|--height) hHelp true ;;
|
||||||
|
-s|--step) sHelp true ;;
|
||||||
|
# -l|--overlap) lHelp true ;;
|
||||||
|
-p|--options) pHelp true ;;
|
||||||
|
-g|--gravity) gHelp true ;;
|
||||||
|
-x|--extent) xHelp true ;;
|
||||||
|
-d|--dzi) dHelp true ;;
|
||||||
|
-a|--slicea) aHelp true ;;
|
||||||
|
-b|--sliceb) bHelp true ;;
|
||||||
|
-c|--scaleover) cHelp true ;;
|
||||||
|
-r|--horizontal) rHelp true ;;
|
||||||
|
-t|--vertical) tHelp true ;;
|
||||||
|
-v|--verbose) vHelp true ;;
|
||||||
|
-u|--usage) uHelp true ;;
|
||||||
|
-\?|--help) qHelp true ;;
|
||||||
|
-m|--man) mHelp true ;;
|
||||||
|
|
||||||
|
"")
|
||||||
|
uHelp $2
|
||||||
|
echo " Options list: "
|
||||||
|
qHelp $2
|
||||||
|
mHelp $2
|
||||||
|
vHelp $2
|
||||||
|
iHelp $2
|
||||||
|
oHelp $2
|
||||||
|
eHelp $2
|
||||||
|
wHelp $2
|
||||||
|
hHelp $2
|
||||||
|
sHelp $2
|
||||||
|
# lHelp $2
|
||||||
|
pHelp $2
|
||||||
|
gHelp $2
|
||||||
|
xHelp $2
|
||||||
|
dHelp $2
|
||||||
|
aHelp $2
|
||||||
|
bHelp $2
|
||||||
|
cHelp $2
|
||||||
|
rHelp $2
|
||||||
|
tHelp $2
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
echo " Unknown option: $1"
|
||||||
|
echo
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# ### CLI parsing ###
|
||||||
|
debugMsg "CLI parsing"
|
||||||
|
|
||||||
|
# Test number of arguments
|
||||||
|
if [ $# -eq 0 ]
|
||||||
|
then
|
||||||
|
cliHelp
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Temp variables for parser
|
||||||
|
WnotDefined=true
|
||||||
|
HnotDefined=true
|
||||||
|
SourceNotDefined=true
|
||||||
|
ResDirNotDefined=true
|
||||||
|
ExtNotDefined=true
|
||||||
|
|
||||||
|
# Arguments parsing
|
||||||
|
while [[ $# > 0 ]] # cmd tools
|
||||||
|
do
|
||||||
|
debugMsg "Parsing key: $1"
|
||||||
|
key="$1" # Get current key
|
||||||
|
case $key in # Do key work
|
||||||
|
|
||||||
|
-i|--in)
|
||||||
|
imageSource="$2"
|
||||||
|
SourceNotDefined=false
|
||||||
|
shift # past argument
|
||||||
|
;;
|
||||||
|
|
||||||
|
-o|--out)
|
||||||
|
resultDir="$2"
|
||||||
|
ResDirNotDefined=false
|
||||||
|
shift # past argument
|
||||||
|
;;
|
||||||
|
|
||||||
|
-e|--extension)
|
||||||
|
resultExt="$2"
|
||||||
|
ExtNotDefined=false
|
||||||
|
shift # past argument
|
||||||
|
;;
|
||||||
|
|
||||||
|
-w|--width)
|
||||||
|
tileW="$2"
|
||||||
|
if $HnotDefined
|
||||||
|
then
|
||||||
|
tileH="$2" # Set h=w by default, if it not defined yet
|
||||||
|
fi
|
||||||
|
WnotDefined=false
|
||||||
|
shift # past argument
|
||||||
|
;;
|
||||||
|
|
||||||
|
-h|--height)
|
||||||
|
tileH="$2"
|
||||||
|
if $WnotDefined
|
||||||
|
then
|
||||||
|
tileW="$2" # Set w=h by default, if it not defined yet
|
||||||
|
fi
|
||||||
|
HnotDefined=false
|
||||||
|
shift # past argument
|
||||||
|
;;
|
||||||
|
|
||||||
|
-s|--step)
|
||||||
|
step="$2"
|
||||||
|
shift # past argument
|
||||||
|
;;
|
||||||
|
|
||||||
|
-l|--overlap)
|
||||||
|
overlap="$2"
|
||||||
|
shift # past argument
|
||||||
|
;;
|
||||||
|
|
||||||
|
-p|--options)
|
||||||
|
imOptions="$2"
|
||||||
|
shift # past argument
|
||||||
|
;;
|
||||||
|
|
||||||
|
-g|--gravity)
|
||||||
|
gravity="$2"
|
||||||
|
shift # past argument
|
||||||
|
;;
|
||||||
|
|
||||||
|
-x|--extent)
|
||||||
|
extent=true
|
||||||
|
;;
|
||||||
|
|
||||||
|
-d|--dzi)
|
||||||
|
dziFormat=true
|
||||||
|
;;
|
||||||
|
|
||||||
|
-a|--slicea)
|
||||||
|
scaleFromImage=true
|
||||||
|
;;
|
||||||
|
|
||||||
|
-b|--sliceb)
|
||||||
|
scaleFromImage=false
|
||||||
|
;;
|
||||||
|
|
||||||
|
-c|--scaleover)
|
||||||
|
scaleover=false
|
||||||
|
;;
|
||||||
|
|
||||||
|
-r|--horizontal)
|
||||||
|
horizontal=true
|
||||||
|
;;
|
||||||
|
|
||||||
|
-t|--vertical)
|
||||||
|
horizontal=false
|
||||||
|
;;
|
||||||
|
|
||||||
|
-v|--verbose)
|
||||||
|
verboseLevel="$2"
|
||||||
|
shift # past argument
|
||||||
|
;;
|
||||||
|
|
||||||
|
-v0|--verbose0)
|
||||||
|
verboseLevel=0
|
||||||
|
;;
|
||||||
|
|
||||||
|
-v1|--verbose1)
|
||||||
|
verboseLevel=1
|
||||||
|
;;
|
||||||
|
|
||||||
|
-v2|--verbose1)
|
||||||
|
verboseLevel=2
|
||||||
|
;;
|
||||||
|
|
||||||
|
-v3|--verbose1)
|
||||||
|
verboseLevel=3
|
||||||
|
;;
|
||||||
|
|
||||||
|
-u|--usage)
|
||||||
|
uHelp
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
|
||||||
|
-\?|--help)
|
||||||
|
cliHelp "$2"
|
||||||
|
shift # past argument
|
||||||
|
;;
|
||||||
|
|
||||||
|
-m|--man)
|
||||||
|
cliHelp "" true
|
||||||
|
;;
|
||||||
|
|
||||||
|
# --default)
|
||||||
|
# DEFAULT=YES
|
||||||
|
# ;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
if $SourceNotDefined # Interpreting first unknown command as source
|
||||||
|
then
|
||||||
|
imageSource="$1"
|
||||||
|
SourceNotDefined=false
|
||||||
|
else
|
||||||
|
if $ResDirNotDefined # Interpreting second unknown command as source
|
||||||
|
then
|
||||||
|
resultDir="$1"
|
||||||
|
ResDirNotDefined=false
|
||||||
|
else # Interpreting third unknown command as unknown command
|
||||||
|
echo "Unknown option: $1"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
shift # Get next key
|
||||||
|
done
|
||||||
|
|
||||||
|
# Cheking for installed applications
|
||||||
|
command -v convert >/dev/null 2>&1 || { echo >&2 "I require ImageMagick tool 'convert', but it's not installed. Aborting."; exit 1; }
|
||||||
|
command -v identify >/dev/null 2>&1 || { echo >&2 "I require ImageMagick tool 'identify', but it's not installed. Aborting."; exit 1; }
|
||||||
|
|
||||||
|
# Checking if file was defined
|
||||||
|
if $SourceNotDefined
|
||||||
|
then
|
||||||
|
echo "No source file present. Canceled."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Cheking if source file not exists
|
||||||
|
if [ ! -f "$imageSource" ]
|
||||||
|
then
|
||||||
|
echo "Error! Input file 'images source' not found! File path: $imageSource"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set extension
|
||||||
|
fullName=$(basename "$imageSource")
|
||||||
|
fileBase="${fullName%.*}"
|
||||||
|
fileExt="${fullName##*.}"
|
||||||
|
|
||||||
|
if $ExtNotDefined
|
||||||
|
then
|
||||||
|
resultExt="$fileExt"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set out name
|
||||||
|
if $ResDirNotDefined
|
||||||
|
then
|
||||||
|
resultDir="$fileBase"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ———————————————————————————————————————————————————————————————————————————————————
|
||||||
|
# ####### Functions ####### #
|
||||||
|
debugMsg "Section: Functions"
|
||||||
|
|
||||||
|
getImgW(){ # image_file
|
||||||
|
echo `identify -format "%[fx:w]" $1`
|
||||||
|
}
|
||||||
|
|
||||||
|
getImgH(){ # image_file
|
||||||
|
echo `identify -format "%[fx:h]" $1`
|
||||||
|
}
|
||||||
|
|
||||||
|
# ———————————————————————————————————————————————————————————————————————————————————
|
||||||
|
# ######################## #
|
||||||
|
# ####### Slicer A ####### #
|
||||||
|
debugMsg "Section: Slicer A"
|
||||||
|
|
||||||
|
# Constants
|
||||||
|
scaleBase=100 # Scale in percents - 100% (TODO: add option to use image sizes)
|
||||||
|
scaleMult=100000 # Scale multiplier (bash works only with int)
|
||||||
|
scaleMult64=100000000000000 # Scale multiplier for x64 bash and x64 convert (if you have very many zoom level and need more accuracy)
|
||||||
|
scaleStart=0
|
||||||
|
# declare -a scaledW
|
||||||
|
# declare -a scaledH
|
||||||
|
# scaledW=()
|
||||||
|
# scaledH=()
|
||||||
|
|
||||||
|
setScale(){
|
||||||
|
if $scale64
|
||||||
|
then
|
||||||
|
local arch=`uname -m`
|
||||||
|
if [ "${arch}" == "x86_64" ]
|
||||||
|
then
|
||||||
|
scaleMult=$scaleMult64
|
||||||
|
else
|
||||||
|
echo "Your system (${arch}) isn't x86_64"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
scaleStart=$(( $scaleBase * $scaleMult ))
|
||||||
|
}
|
||||||
|
|
||||||
|
getZoomLevels(){ # imgLen(pixels) tileLen(pixels) step(int) # Calculate zoom levels for current step
|
||||||
|
local imgLen=$1
|
||||||
|
local tileLen=$2
|
||||||
|
local zoomStep=$3
|
||||||
|
local r=(0)
|
||||||
|
local cnt=1
|
||||||
|
|
||||||
|
# Drop zooms less tile size
|
||||||
|
# while [ "$imgLen" -gt "$tileLen" ]
|
||||||
|
# do
|
||||||
|
# r[$cnt]=$imgLen
|
||||||
|
# let "cnt+=1"
|
||||||
|
# let "imgLen = imgLen * 100 / zoomStep"
|
||||||
|
# done
|
||||||
|
|
||||||
|
# Do all zooms down to 1x1 px
|
||||||
|
while [ "$imgLen" -ge 1 ]
|
||||||
|
do
|
||||||
|
r[$cnt]=$imgLen
|
||||||
|
let "cnt+=1"
|
||||||
|
let "imgLen = imgLen * 100 / zoomStep"
|
||||||
|
done
|
||||||
|
|
||||||
|
r[$cnt]=$imgLen
|
||||||
|
r[0]=$cnt
|
||||||
|
echo ${r[*]}
|
||||||
|
}
|
||||||
|
|
||||||
|
nextScale(){ # oldScale -> newScale
|
||||||
|
# Calculate image zoom in percents - it need for imagemagick for image resize
|
||||||
|
echo $(( $1 * 100 / $step ))
|
||||||
|
}
|
||||||
|
|
||||||
|
scaleToPercents(){ # scale
|
||||||
|
local s=$1
|
||||||
|
local sInt=0
|
||||||
|
local sFloat=0
|
||||||
|
let "sInt = s / $scaleMult"
|
||||||
|
let "sFloat = s - sInt * $scaleMult"
|
||||||
|
echo "${sInt}.${sFloat}%"
|
||||||
|
}
|
||||||
|
|
||||||
|
# scaleImage(){ # zoom scale -> file_path
|
||||||
|
# local zoom=$1
|
||||||
|
# local s=$2
|
||||||
|
# local dir="${resultDir}/${zoom}"
|
||||||
|
# local file="${dir}.${resultExt}"
|
||||||
|
# local size=`scaleToPercents $s`
|
||||||
|
# mkdir -p $dir # Imagemagick can't create directories
|
||||||
|
# convert $imageSource $resizeFilter -resize $size $file
|
||||||
|
# echo $file
|
||||||
|
# }
|
||||||
|
|
||||||
|
zoomImage(){ # zoom size -> file_path
|
||||||
|
local zoom=$1
|
||||||
|
local size=$2
|
||||||
|
local dir="${resultDir}/${zoom}"
|
||||||
|
local file="${dir}.${resultExt}"
|
||||||
|
# local file="${dir}.png"
|
||||||
|
# local size=`scaleToPercents $s`
|
||||||
|
mkdir -p $dir # Imagemagick can't create directories
|
||||||
|
convert $imageSource $resizeFilter -resize $size $imOptions $file
|
||||||
|
echo $file
|
||||||
|
}
|
||||||
|
|
||||||
|
sliceImage(){ # zoom image
|
||||||
|
local zoom=$1
|
||||||
|
local src=$2
|
||||||
|
local wxh="${tileW}x${tileH}"
|
||||||
|
|
||||||
|
local xyDelim='/'
|
||||||
|
if $dziFormat
|
||||||
|
then
|
||||||
|
xyDelim='_'
|
||||||
|
fi
|
||||||
|
|
||||||
|
local tilesFormat="%[fx:page.x/${tileW}]${xyDelim}%[fx:page.y/${tileH}]" # This very important magic! It allow imagemagick to generate tile names with it position and place it in corect folders (but folders need to generate manually)
|
||||||
|
local file="${resultDir}/${zoom}/%[filename:tile].${resultExt}"
|
||||||
|
|
||||||
|
if [ ! $dziFormat ]
|
||||||
|
then
|
||||||
|
# Creating subdirectories for tiles (one vertical line of tiles is in one folder)
|
||||||
|
local srcSize=`getImgW $src` # Getting image width
|
||||||
|
local dirNum=$(( $srcSize / $tileW )) # Calculating number of tiles in line
|
||||||
|
local i=0
|
||||||
|
for(( i=0; i<=$dirNum; i++ ))
|
||||||
|
do
|
||||||
|
mkdir -p "${resultDir}/${zoom}/$i" # Imagemagick can't create directories
|
||||||
|
done
|
||||||
|
sync
|
||||||
|
fi
|
||||||
|
|
||||||
|
# extent option
|
||||||
|
local ext=''
|
||||||
|
if $extent
|
||||||
|
then
|
||||||
|
ext="-background none -extent ${wxh}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Slice image to tiles
|
||||||
|
# convert $src -crop $wxh -set filename:tile $tilesFormat +repage +adjoin -background none -gravity $gravity $ext $file
|
||||||
|
convert $src -gravity $gravity -crop $wxh -set filename:tile $tilesFormat +repage +adjoin -gravity $gravity $ext $file
|
||||||
|
}
|
||||||
|
|
||||||
|
sliceA(){
|
||||||
|
infoMsg " Slicer A is running..."
|
||||||
|
local scalesW=( `getZoomLevels $imageW $tileW $step` ) # Get width for each zoom level
|
||||||
|
local scalesH=( `getZoomLevels $imageH $tileH $step` ) # Get height for each zoom level
|
||||||
|
local zw=${scalesW[0]} # Get zoom level for width
|
||||||
|
local zh=${scalesH[0]} # Get zoom level for height
|
||||||
|
local scales=() # Creating empty array
|
||||||
|
local zoomMax=0
|
||||||
|
local zoom=0
|
||||||
|
local hMod=''
|
||||||
|
local s=0
|
||||||
|
local file=''
|
||||||
|
|
||||||
|
if [ "$zw" -ge "$zh" ]
|
||||||
|
then
|
||||||
|
zoomMax=$zw
|
||||||
|
scales=( ${scalesW[*]} )
|
||||||
|
hMod=''
|
||||||
|
else
|
||||||
|
zoomMax=$zh
|
||||||
|
scales=( ${scalesH[*]} )
|
||||||
|
hMod='x'
|
||||||
|
fi
|
||||||
|
|
||||||
|
# local scale=$scaleStart
|
||||||
|
# local scalep=''
|
||||||
|
while [ "$s" -lt "$zoomMax" ]
|
||||||
|
do
|
||||||
|
if $zoomReverse
|
||||||
|
then
|
||||||
|
let "zoom = s"
|
||||||
|
else
|
||||||
|
let "zoom = zoomMax - s"
|
||||||
|
fi
|
||||||
|
|
||||||
|
infoMsg " Resizing next file..."
|
||||||
|
debugMsg " zoomMax=$zoomMax, zoomLevel=$s, wxhInex=$zoom, wxh=${hMod}${scales[$zoom]}"
|
||||||
|
file=`zoomImage $s "${hMod}${scales[$zoom]}"`
|
||||||
|
infoMsg " File resized: ${file}"
|
||||||
|
infoMsg " Slicing file..."
|
||||||
|
sliceImage $s $file
|
||||||
|
rm -rf $file
|
||||||
|
|
||||||
|
# scalep=`scaleToPercents $scale`
|
||||||
|
# s=${scales[zoom-1]}
|
||||||
|
# echo $zoom "$s"
|
||||||
|
# file=`scaleImage $zoom "${hMod}${scales[$zoom]}"`
|
||||||
|
# echo "zoom, scalep, scale: $zoom $scalep $scale $file"
|
||||||
|
# echo ${scaledW[$i]}
|
||||||
|
# echo ${scaledH[$i]}
|
||||||
|
# scale=`nextScale $scale`
|
||||||
|
|
||||||
|
let "s+=1"
|
||||||
|
done
|
||||||
|
|
||||||
|
infoMsg " Slicer A complete"
|
||||||
|
|
||||||
|
# s=`nextScale $scaleStart`
|
||||||
|
# s=`nextScale $s`
|
||||||
|
# scaleToPercents $s
|
||||||
|
}
|
||||||
|
|
||||||
|
# ———————————————————————————————————————————————————————————————————————————————————
|
||||||
|
# ##########################
|
||||||
|
# ####### Slicer B ####### #
|
||||||
|
debugMsg "Section: Slicer B"
|
||||||
|
|
||||||
|
zoomPixels(){ # zoom tileSize
|
||||||
|
local zoom=$1
|
||||||
|
local pixels=$2
|
||||||
|
if [ "$zoom" -ne 0 ]
|
||||||
|
then
|
||||||
|
let "pixels = pixels * 100"
|
||||||
|
for(( i=0; i<zoom; i++ ))
|
||||||
|
do
|
||||||
|
let "pixels = pixels * $step / 100"
|
||||||
|
done
|
||||||
|
let "pixels = pixels / 100"
|
||||||
|
fi
|
||||||
|
echo $pixels
|
||||||
|
}
|
||||||
|
|
||||||
|
resizeImageH(){ # zoom -> file_path
|
||||||
|
local zoom=$1
|
||||||
|
local dir="${resultDir}/${zoom}"
|
||||||
|
local file="${dir}.${resultExt}"
|
||||||
|
local size=`zoomPixels $zoom $tileW`
|
||||||
|
mkdir -p $dir # Imagemagick can't create directories
|
||||||
|
convert $imageSource $resizeFilter -resize $size $imOptions $file
|
||||||
|
echo $file
|
||||||
|
}
|
||||||
|
|
||||||
|
resizeImageV(){ # zoom -> file_path
|
||||||
|
local zoom=$1
|
||||||
|
local dir="${resultDir}/${zoom}"
|
||||||
|
local file="${dir}.${resultExt}"
|
||||||
|
local size=`zoomPixels $zoom $tileH`
|
||||||
|
mkdir -p $dir # Imagemagick can't create directories
|
||||||
|
convert $imageSource $resizeFilter -resize "x${size}" $imOptions $file
|
||||||
|
echo $file
|
||||||
|
}
|
||||||
|
|
||||||
|
resizeImage(){ # zoom -> file_path
|
||||||
|
if $horizontal
|
||||||
|
then
|
||||||
|
echo `resizeImageH $1`
|
||||||
|
else
|
||||||
|
echo `resizeImageV $1`
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
sliceB(){
|
||||||
|
echo "Slicer B is running..."
|
||||||
|
local size=0
|
||||||
|
local sizeMax=0
|
||||||
|
local zoom=0
|
||||||
|
|
||||||
|
if $horizontal
|
||||||
|
then
|
||||||
|
let "size = $tileW"
|
||||||
|
let "sizeMax = $imageW"
|
||||||
|
else
|
||||||
|
let "size = $tileH"
|
||||||
|
let "sizeMax = $imageH"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if $scaleover
|
||||||
|
then
|
||||||
|
let "sizeMax += $size"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local px=$size
|
||||||
|
while [ "$px" -lt "$sizeMax" ]
|
||||||
|
do
|
||||||
|
echo " Slicing zoom level \"${zoom}\"; image main size is \"${px}\""
|
||||||
|
sliceImage $zoom `resizeImage $zoom`
|
||||||
|
let "zoom++"
|
||||||
|
px=`zoomPixels $zoom $size`
|
||||||
|
done
|
||||||
|
echo "Slicer B complete"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
mainScale(){ # min zoom = tile width
|
||||||
|
if $scaleFromImage
|
||||||
|
then
|
||||||
|
sliceA
|
||||||
|
else
|
||||||
|
sliceB
|
||||||
|
fi
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
setDziFormat(){
|
||||||
|
local dziFileName="${resultDir}.dzi"
|
||||||
|
resultDir="${resultDir}_files"
|
||||||
|
tileH=$tileW
|
||||||
|
|
||||||
|
mkdir -p "$(dirname "${dziFileName}")"
|
||||||
|
touch ${dziFileName}
|
||||||
|
|
||||||
|
echo '<?xml version="1.0"?>' > "$dziFileName"
|
||||||
|
echo "<Image TileSize=\"${tileW}\" Overlap=\"0\" Format=\"${resultExt}\" xmlns=\"http://schemas.microsoft.com/deepzoom/2008\">" >> "$dziFileName"
|
||||||
|
echo "<Size Width=\"${imageW}\" Height=\"${imageH}\"/>" >> "$dziFileName"
|
||||||
|
echo '</Image>' >> "$dziFileName"
|
||||||
|
# <?xml version="1.0"?>
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# </Image>
|
||||||
|
}
|
||||||
|
|
||||||
|
setFormat(){
|
||||||
|
if $dziFormat
|
||||||
|
then
|
||||||
|
setDziFormat
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
init(){
|
||||||
|
if [ "$step" -le 100 ]
|
||||||
|
then
|
||||||
|
echo "You get infinity loop. Minimum step value = 101% (101)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# Getting image sizes
|
||||||
|
imageW=`getImgW $imageSource`
|
||||||
|
imageH=`getImgH $imageSource`
|
||||||
|
|
||||||
|
# Set options for selected format
|
||||||
|
setFormat
|
||||||
|
|
||||||
|
# Set scale
|
||||||
|
setScale
|
||||||
|
|
||||||
|
rm -rf $resultDir # removing old results
|
||||||
|
mkdir -p $resultDir # creating new results folder
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# ———————————————————————————————————————————————————————————————————————————————————
|
||||||
|
# ###################### #
|
||||||
|
# ### Programm start ### #
|
||||||
|
debugMsg "Section: Programm"
|
||||||
|
|
||||||
|
init
|
||||||
|
mainScale
|
||||||
|
|
||||||
|
# ### Programm end ##### #
|
||||||
|
# ###################### #
|
||||||
|
# ———————————————————————————————————————————————————————————————————————————————————
|
|
@ -0,0 +1,67 @@
|
||||||
|
# ensure to use my fork https://github.com/wattapik/MagickSlicer
|
||||||
|
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
def convert_to_jpg(input_file, output_dir):
|
||||||
|
try:
|
||||||
|
# Construct the output file path for the converted JPG
|
||||||
|
file_name_without_extension = os.path.splitext(os.path.basename(input_file))[0]
|
||||||
|
output_file = os.path.join("original", "jpg", f"{file_name_without_extension}.jpg")
|
||||||
|
|
||||||
|
# Command to convert the image to JPG using ImageMagick's 'convert' command
|
||||||
|
convert_command = ["convert", input_file, "-quality", "100", output_file]
|
||||||
|
|
||||||
|
# Run the conversion command
|
||||||
|
subprocess.run(convert_command)
|
||||||
|
print(f"Converted {input_file} to JPG")
|
||||||
|
|
||||||
|
# Once converted, process the JPG image
|
||||||
|
process_image(output_file, output_dir)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error converting {input_file} to JPG: {e}")
|
||||||
|
|
||||||
|
def process_image(input_file, output_dir):
|
||||||
|
try:
|
||||||
|
# Construct the output file path
|
||||||
|
file_name_without_extension = os.path.splitext(os.path.basename(input_file))[0]
|
||||||
|
output_file = os.path.join(output_dir, file_name_without_extension, f"{file_name_without_extension}")
|
||||||
|
|
||||||
|
# Command to invoke the "magick-slicer.sh" script
|
||||||
|
command = ["./magick-slicer.sh", input_file, output_file]
|
||||||
|
|
||||||
|
print(command)
|
||||||
|
print(output_file)
|
||||||
|
print(os.getcwd())
|
||||||
|
# Run the command
|
||||||
|
subprocess.run(command)
|
||||||
|
print(f"Processed {input_file}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error processing {input_file}: {e}")
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# Define the input and output directories
|
||||||
|
input_dir = "original"
|
||||||
|
output_dir = "dzi"
|
||||||
|
|
||||||
|
# Ensure the output directory exists
|
||||||
|
os.makedirs(output_dir, exist_ok=True)
|
||||||
|
os.makedirs(os.path.join(input_dir, "jpg"), exist_ok=True) # Create "original/jpg" folder
|
||||||
|
|
||||||
|
# Gather a list of image files in the input directory
|
||||||
|
image_files = []
|
||||||
|
for root, _, files in os.walk(input_dir):
|
||||||
|
for filename in files:
|
||||||
|
if filename.lower().endswith((".jpg", ".jpeg", ".png", ".gif")):
|
||||||
|
image_files.append(os.path.join(root, filename))
|
||||||
|
|
||||||
|
# Convert and process image files synchronously
|
||||||
|
for input_file in image_files:
|
||||||
|
convert_to_jpg(input_file, output_dir)
|
||||||
|
|
||||||
|
# Delete the "original/jpg" folder once all conversions and processing are finished
|
||||||
|
# shutil.rmtree(os.path.join(input_dir, "jpg"))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
After Width: | Height: | Size: 15 KiB |
|
@ -0,0 +1,109 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<!--
|
||||||
|
.:/+++oo+/:.
|
||||||
|
`:++:.` `s--s/.:++-
|
||||||
|
`/o- `s- /o. `:o/
|
||||||
|
-s: `s: .o/ :s.
|
||||||
|
-s` `s: :s- .s.
|
||||||
|
`y. o/ `o+` -s
|
||||||
|
:o o+ -s: s:
|
||||||
|
++ ++ `/oo/
|
||||||
|
/o +o `.+y:
|
||||||
|
`y. /o `-:/++/:/y`
|
||||||
|
-s` /o` `-:+++/-. .s-
|
||||||
|
-s/s`.-/+++/-. -s-
|
||||||
|
`+ss/-` -o/`
|
||||||
|
`:o+-. .:+o:`
|
||||||
|
`-:++++++++:-`
|
||||||
|
-->
|
||||||
|
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Sketchers United Collaboration</title>
|
||||||
|
|
||||||
|
<link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png">
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="/favicon/favicon-16x16.png">
|
||||||
|
<link rel="manifest" href="/favicon/site.webmanifest">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="script/openseadragon/annotorious.min.css">
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
|
||||||
|
<script src="script/caman/caman.full.min.js"></script>
|
||||||
|
<script src="script/openseadragon/openseadragon.min.js"></script>
|
||||||
|
<script src="script/openseadragon/openseadragon-filtering.js"></script>
|
||||||
|
<script src="script/openseadragon/openseadragon-smartScrollZoom.js"></script>
|
||||||
|
<script src="script/openseadragon/openseadragon-annotorious.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="viewer"></div>
|
||||||
|
|
||||||
|
<!-- MODAL -->
|
||||||
|
<div id="modal-overlay"></div>
|
||||||
|
|
||||||
|
<div id="modal">
|
||||||
|
<div id="modal-content">
|
||||||
|
<span id="close-button">×</span>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<img src = "image/banner.png" id = "banner" alt="A banner for the Sketchers United 4th anniversary collab.">
|
||||||
|
<h1>Sketchers United 4th Anniversary Collaboration</h1>
|
||||||
|
|
||||||
|
<p>❤️🧡💛💚💙💜
|
||||||
|
<h2>❓ What is this?</h2>
|
||||||
|
<p>For the 4th anniversary of Sketchers United, we are going to (attempt to) line up submitted artwork from Sketchers United members such that it resembles a gigantic Sketchers United logo.
|
||||||
|
<br>The shape won't seem very evident at the beginning, however as more submissions are made the more we can approximate the shape.
|
||||||
|
<br>You can use the view here to look at the submitted artworks and view the construction of the collaborative Sketchers United logo.
|
||||||
|
<br>
|
||||||
|
<br>We aim to update the view every few days with the newest submissions.</p>
|
||||||
|
|
||||||
|
<h2>🌐 How do I use this page?</h2>
|
||||||
|
<p>Use either the mouse wheel or your fingers to zoom in and out of the view.
|
||||||
|
<br> You can tap on the image to see who drew it and get a link to the post.
|
||||||
|
<br>
|
||||||
|
<br>ℹ️ The view may get somewhat laggy on a mobile device. For the best experience use a desktop computer.
|
||||||
|
<br>This is unavoidable due to the number of images downloaded and shown.
|
||||||
|
<br>It may also fail to load images correctly on Chrome on newer versions of Android, leading to the page freezing. In this case, use the Firefox browser or a desktop computer.
|
||||||
|
|
||||||
|
<h2>☝️ How do I join?</h2>
|
||||||
|
<p><b>📩 Submit an entry under the hashtag <a href = "https://sketchersunited.org/tags/sucollab" class = "rainbow">#sucollab</a> to enter.</b>
|
||||||
|
<br>Entries can contain any subject matter however inappropriate entries will be discarded.
|
||||||
|
|
||||||
|
<p><b> You can submit a single entry on your own.</b>
|
||||||
|
<br>You are allowed to submit more than one entry if you are working with friends to submit a collaborative entry.
|
||||||
|
<br>For example, you can submit an entry with only your own artwork, and then submit another entry which contains your work and the work of others that was created collaboratively.
|
||||||
|
|
||||||
|
<p><b>⚠️ Make sure that it is square and at least 1000 pixels wide and high.</b>
|
||||||
|
<br>We will be using all of the entries where possible, which means if your entry does not fit these requirements it will be cropped and upscaled, which may be destructive to your art.</p>
|
||||||
|
|
||||||
|
<h2>💌 Who can I contact about this?</h2>
|
||||||
|
<p>General questions about the collaboration can be sent to <a href = "https://sketchersunited.org/users/12271" class = "rainbow">@pelunkey</a> and <a href = "https://sketchersunited.org/users/9" class = "rainbow">@username</a>.
|
||||||
|
<br>This website is run by <a href ="https://sketchersunited.org/users/9" class = "rainbow">@username</a>. If there are any technical issues with the site please send me a message.
|
||||||
|
<br>The website is still under construction and there may be issues.</p>
|
||||||
|
|
||||||
|
<p>❤️🧡💛💚💙💜
|
||||||
|
<br><br>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- USER INDICATOR -->
|
||||||
|
<div id="here-now">
|
||||||
|
🌈<b class = "rainbow"> Here now:</b> <span id="clientCount">0</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--- OPEN MODAL BUTTON -->
|
||||||
|
<a id = "open-button">
|
||||||
|
<div id="question">
|
||||||
|
<span class = "rainbow">❓</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!--- SCRIPTS -->
|
||||||
|
<script src="script/modal.js"></script>
|
||||||
|
<script src="script/live.js"></script>
|
||||||
|
<script src="script/viewer.js"></script>
|
||||||
|
<script src="script/image_load.js"></script>
|
||||||
|
<script src="script/annotations.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,28 @@
|
||||||
|
const anno = OpenSeadragon.Annotorious(viewer);
|
||||||
|
anno.readOnly = true
|
||||||
|
anno.loadAnnotations('annotations/initial.w3c.json');
|
||||||
|
let annotationText = ""
|
||||||
|
|
||||||
|
function renderHTML()
|
||||||
|
{
|
||||||
|
const observer = new MutationObserver(() => {
|
||||||
|
const annotationTextElement = document.querySelector('.r6o-readonly-comment');
|
||||||
|
if (annotationTextElement) {
|
||||||
|
annotationTextElement.innerHTML = annotationText
|
||||||
|
observer.disconnect();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
observer.observe(document.body, { childList: true, subtree: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
anno.on('clickAnnotation', function(annotation, element)
|
||||||
|
{
|
||||||
|
annotationText = annotation.body[0].value
|
||||||
|
renderHTML()
|
||||||
|
});
|
||||||
|
|
||||||
|
viewer.addHandler('update-viewport', function(event)
|
||||||
|
{
|
||||||
|
renderHTML()
|
||||||
|
});
|
|
@ -0,0 +1,206 @@
|
||||||
|
|
||||||
|
(function(){var $,Analyze,Blender,Calculate,Caman,CamanInstance,CamanParser,Canvas,Convert,Event,Filter,IO,Image,Layer,Log,Logger,PixelInfo,Plugin,RenderJob,Root,Store,Util,fs,slice,vignetteFilters,__hasProp={}.hasOwnProperty,__indexOf=[].indexOf||function(item){for(var i=0,l=this.length;i<l;i++){if(i in this&&this[i]===item)return i;}return-1;},__slice=[].slice;slice=Array.prototype.slice;$=function(sel,root){if(root==null){root=document;}
|
||||||
|
if(typeof sel==="object"){return sel;}
|
||||||
|
return root.querySelector(sel);};Util=(function(){function Util(){}
|
||||||
|
Util.uniqid=(function(){var id;id=0;return{get:function(){return id++;}};})();Util.extend=function(obj){var copy,dest,prop,src,_i,_len;dest=obj;src=slice.call(arguments,1);for(_i=0,_len=src.length;_i<_len;_i++){copy=src[_i];for(prop in copy){if(!__hasProp.call(copy,prop))continue;dest[prop]=copy[prop];}}
|
||||||
|
return dest;};Util.clampRGB=function(val){if(val<0){return 0;}
|
||||||
|
if(val>255){return 255;}
|
||||||
|
return val;};return Util;})();if(typeof exports!=="undefined"&&exports!==null){Root=exports;Canvas=require('canvas');Image=Canvas.Image;fs=require('fs');}else{Root=window;}
|
||||||
|
Root.Caman=Caman=function(){if(arguments.length===0){throw"Invalid arguments given";}
|
||||||
|
if(typeof exports!=="undefined"&&exports!==null){return new CamanInstance(arguments,CamanInstance.Type.Node);}
|
||||||
|
switch(arguments.length){case 1:if(Store.has(arguments[0])){return Store.get(arguments[0]);}
|
||||||
|
return new CamanInstance(arguments,CamanInstance.Type.Unknown);case 2:if(Store.has(arguments[0])){return Store.execute(arguments[0],arguments[1]);}
|
||||||
|
if(typeof arguments[1]==='function'){return new CamanInstance(arguments,CamanInstance.Type.Unknown);}else{return new CamanInstance(arguments,CamanInstance.Type.Canvas);}
|
||||||
|
break;case 3:if(Store.has(arguments[0])){return Store.execute(arguments[1],arguments[2]);}
|
||||||
|
return new CamanInstance(arguments,CamanInstance.Type.Canvas);}};Caman.version={release:"3.3.0",date:"12/13/12"};Caman.DEBUG=false;Caman.autoload=!(typeof exports!=="undefined"&&exports!==null);Caman.crossOrigin="anonymous";Caman.toString=function(){return"Version "+Caman.version.release+", Released "+Caman.version.date;};Caman.remoteProxy="";Caman.Util=Util;CamanInstance=(function(){CamanInstance.Type={Image:1,Canvas:2,Unknown:3,Node:4};CamanInstance.toString=Caman.toString;function CamanInstance(args,type){var listener,_this=this;if(type==null){type=CamanInstance.Type.Canvas;}
|
||||||
|
this.id=Util.uniqid.get();this.analyze=new Analyze(this);this.originalPixelData=[];this.pixelStack=[];this.layerStack=[];this.renderQueue=[];this.canvasQueue=[];this.currentLayer=null;if(type===CamanInstance.Type.Node){this.loadNode.apply(this,args);}else{if(document.readyState==="complete"){this.domLoaded(args,type);}else{listener=function(){if(document.readyState==="complete"){return _this.domLoaded(args,type);}};document.addEventListener("readystatechange",listener,false);}}}
|
||||||
|
CamanInstance.prototype.domLoaded=function(args,type){switch(type){case CamanInstance.Type.Image:return this.loadImage.apply(this,args);case CamanInstance.Type.Canvas:return this.loadCanvas.apply(this,args);case CamanInstance.Type.Unknown:return this.loadUnknown(args);}};CamanInstance.prototype.loadUnknown=function(args){var e;e=$(args[0]);switch(e.nodeName.toLowerCase()){case"img":return this.loadImage.apply(this,args);case"canvas":return this.loadCanvas(null,args[0],args[1]);}};CamanInstance.prototype.loadImage=function(id,callback){var image,proxyURL,_ref,_this=this;if(typeof id==="object"&&((_ref=id.nodeName)!=null?_ref.toLowerCase():void 0)==="img"){image=id;if(!image.id){image.id="caman-"+(Util.uniqid.get());}}else{if($(id)!=null){image=$(id);}else{throw"Could not find element "+id;}}
|
||||||
|
proxyURL=IO.remoteCheck(image.src);if(proxyURL){image.onload=function(){return _this.imageLoaded(id,image,callback);};return image.src=proxyURL;}else{if(image.complete){return this.imageLoaded(id,image,callback);}else{return image.onload=function(){return _this.imageLoaded(id,image,callback);};}}};CamanInstance.prototype.imageLoaded=function(id,image,callback){var attr,_i,_len,_ref;this.image=image;if(!image||image.nodeName.toLowerCase()!=="img"){throw"Given element ID isn't an image: "+id;}
|
||||||
|
this.canvas=document.createElement('canvas');this.canvas.id=image.id;_ref=['data-camanwidth','data-camanheight'];for(_i=0,_len=_ref.length;_i<_len;_i++){attr=_ref[_i];if(this.image.getAttribute(attr)){this.canvas.setAttribute(attr,this.image.getAttribute(attr));}}
|
||||||
|
if(image.parentNode!=null){image.parentNode.replaceChild(this.canvas,this.image);}
|
||||||
|
this.canvasID=id;this.options={canvas:id,image:this.image.src};if(this.image.crossOrigin){this.options.crossOrigin=this.image.crossOrigin;}
|
||||||
|
return this.finishInit(callback);};CamanInstance.prototype.loadCanvas=function(url,id,callback){var element,_ref;if(typeof id==="object"&&((_ref=id.nodeName)!=null?_ref.toLowerCase():void 0)==="canvas"){element=id;if(!element.id){element.id="caman-"+(Util.uniqid.get());}}else{if($(id)!=null){element=$(id);}else{throw"Could not find element "+id;}}
|
||||||
|
return this.canvasLoaded(url,element,callback);};CamanInstance.prototype.canvasLoaded=function(url,canvas,callback){var proxyURL,_this=this;this.canvas=canvas;if(!canvas||canvas.nodeName.toLowerCase()!=="canvas"){throw"Given element ID isn't a canvas: "+id;}
|
||||||
|
if(url!=null){this.image=document.createElement('img');this.image.onload=function(){return _this.finishInit(callback);};proxyURL=IO.remoteCheck(url);if(proxyURL){url=proxyURL;}
|
||||||
|
this.canvasID=this.canvas.id;this.options={canvas:canvas.id,image:url};if(IO.isRemote(url)){this.image.crossOrigin=Caman.crossOrigin;}
|
||||||
|
return this.image.src=url;}else{return this.finishInit(callback);}};CamanInstance.prototype.loadNode=function(file,callback){var img,_this=this;img=new Image();if(typeof file==="string"){file=fs.realpathSync(file);}
|
||||||
|
img.onload=function(){var context;_this.canvasID=Util.uniqid.get();_this.canvas=new Canvas(img.width,img.height);context=_this.canvas.getContext('2d');context.drawImage(img,0,0);return _this.finishInit(callback);};img.onerror=function(err){throw err;};return img.src=file;};CamanInstance.prototype.finishInit=function(callback){var newHeight,newWidth,oldHeight,oldWidth,pixel,_i,_len,_ref;if(callback==null){callback=function(){};}
|
||||||
|
this.context=this.canvas.getContext("2d");if(this.image!=null){oldWidth=this.image.width;oldHeight=this.image.height;newWidth=this.canvas.getAttribute('data-camanwidth');newHeight=this.canvas.getAttribute('data-camanheight');if(newWidth||newHeight){if(newWidth){this.image.width=parseInt(newWidth,10);if(newHeight){this.image.height=parseInt(newHeight,10);}else{this.image.height=this.image.width*oldHeight/oldWidth;}}else if(newHeight){this.image.height=parseInt(newHeight,10);this.image.width=this.image.height*oldWidth/oldHeight;}}
|
||||||
|
this.canvas.width=this.image.width;this.canvas.height=this.image.height;if(window.devicePixelRatio){this.canvas.style.width=""+this.image.width+"px";this.canvas.style.height=""+this.image.height+"px";this.canvas.width=this.image.width*window.devicePixelRatio;this.canvas.height=this.image.height*window.devicePixelRatio;this.context.scale(window.devicePixelRatio,window.devicePixelRatio);}
|
||||||
|
this.context.drawImage(this.image,0,0,this.image.width,this.image.height);}
|
||||||
|
this.imageData=this.context.getImageData(0,0,this.canvas.width,this.canvas.height);this.pixelData=this.imageData.data;_ref=this.pixelData;for(_i=0,_len=_ref.length;_i<_len;_i++){pixel=_ref[_i];this.originalPixelData.push(pixel);}
|
||||||
|
this.dimensions={width:this.canvas.width,height:this.canvas.height};Store.put(this.canvasID,this);callback.call(this,this);return this;};CamanInstance.prototype.replaceCanvas=function(newCanvas){var oldCanvas;oldCanvas=this.canvas;this.canvas=newCanvas;if(oldCanvas.parentNode!=null){oldCanvas.parentNode.replaceChild(this.canvas,oldCanvas);}
|
||||||
|
this.context=this.canvas.getContext('2d');this.imageData=this.context.getImageData(0,0,this.canvas.width,this.canvas.height);this.pixelData=this.imageData.data;return this.dimensions={width:this.canvas.width,height:this.canvas.height};};return CamanInstance;})();Analyze=(function(){function Analyze(c){this.c=c;}
|
||||||
|
Analyze.prototype.calculateLevels=function(){var i,levels,numPixels,_i,_j,_k,_ref;levels={r:{},g:{},b:{}};for(i=_i=0;_i<=255;i=++_i){levels.r[i]=0;levels.g[i]=0;levels.b[i]=0;}
|
||||||
|
for(i=_j=0,_ref=this.c.pixelData.length;_j<_ref;i=_j+=4){levels.r[this.c.pixelData[i]]++;levels.g[this.c.pixelData[i+1]]++;levels.b[this.c.pixelData[i+2]]++;}
|
||||||
|
numPixels=this.c.pixelData.length/4;for(i=_k=0;_k<=255;i=++_k){levels.r[i]/=numPixels;levels.g[i]/=numPixels;levels.b[i]/=numPixels;}
|
||||||
|
return levels;};return Analyze;})();Caman.DOMUpdated=function(){var img,imgs,parser,_i,_len,_results;imgs=document.querySelectorAll("img[data-caman]");if(!(imgs.length>0)){return;}
|
||||||
|
_results=[];for(_i=0,_len=imgs.length;_i<_len;_i++){img=imgs[_i];_results.push(parser=new CamanParser(img,function(){this.parse();return this.execute();}));}
|
||||||
|
return _results;};if(Caman.autoload){(function(){if(document.readyState==="complete"){return Caman.DOMUpdated();}else{return document.addEventListener("DOMContentLoaded",Caman.DOMUpdated,false);}})();}
|
||||||
|
CamanParser=(function(){var INST_REGEX;INST_REGEX="(\\w+)\\((.*?)\\)";function CamanParser(ele,ready){this.dataStr=ele.getAttribute('data-caman');this.caman=Caman(ele,ready.bind(this));}
|
||||||
|
CamanParser.prototype.parse=function(){var args,filter,func,inst,instFunc,m,r,unparsedInstructions,_i,_len,_ref,_results;this.ele=this.caman.canvas;r=new RegExp(INST_REGEX,'g');unparsedInstructions=this.dataStr.match(r);if(!(unparsedInstructions.length>0)){return;}
|
||||||
|
r=new RegExp(INST_REGEX);_results=[];for(_i=0,_len=unparsedInstructions.length;_i<_len;_i++){inst=unparsedInstructions[_i];_ref=inst.match(r),m=_ref[0],filter=_ref[1],args=_ref[2];instFunc=new Function("return function() { this."+filter+"("+args+"); };");try{func=instFunc();_results.push(func.call(this.caman));}catch(e){_results.push(Log.debug(e));}}
|
||||||
|
return _results;};CamanParser.prototype.execute=function(){var ele;ele=this.ele;return this.caman.render(function(){return ele.parentNode.replaceChild(this.toImage(),ele);});};return CamanParser;})();Blender=(function(){function Blender(){}
|
||||||
|
Blender.blenders={};Blender.register=function(name,func){return this.blenders[name]=func;};Blender.execute=function(name,rgbaLayer,rgbaParent){return this.blenders[name](rgbaLayer,rgbaParent);};return Blender;})();Caman.Blender=Blender;Calculate=(function(){function Calculate(){}
|
||||||
|
Calculate.distance=function(x1,y1,x2,y2){return Math.sqrt(Math.pow(x2-x1,2)+Math.pow(y2-y1,2));};Calculate.randomRange=function(min,max,getFloat){var rand;if(getFloat==null){getFloat=false;}
|
||||||
|
rand=min+(Math.random()*(max-min));if(getFloat){return rand.toFixed(getFloat);}else{return Math.round(rand);}};Calculate.luminance=function(rgba){return(0.27*rgba.r)+(0.67*rgba.g)+(0.06*rgba.b);};Calculate.bezier=function(start,ctrl1,ctrl2,end,lowBound,highBound){var Ax,Ay,Bx,By,Cx,Cy,bezier,curveX,curveY,i,j,leftCoord,rightCoord,t,x0,x1,x2,x3,y0,y1,y2,y3,_i,_j,_k,_ref,_ref1;x0=start[0];y0=start[1];x1=ctrl1[0];y1=ctrl1[1];x2=ctrl2[0];y2=ctrl2[1];x3=end[0];y3=end[1];bezier={};Cx=parseInt(3*(x1-x0),10);Bx=3*(x2-x1)-Cx;Ax=x3-x0-Cx-Bx;Cy=3*(y1-y0);By=3*(y2-y1)-Cy;Ay=y3-y0-Cy-By;for(i=_i=0;_i<1000;i=++_i){t=i/1000;curveX=Math.round((Ax*Math.pow(t,3))+(Bx*Math.pow(t,2))+(Cx*t)+x0);curveY=Math.round((Ay*Math.pow(t,3))+(By*Math.pow(t,2))+(Cy*t)+y0);if(lowBound&&curveY<lowBound){curveY=lowBound;}else if(highBound&&curveY>highBound){curveY=highBound;}
|
||||||
|
bezier[curveX]=curveY;}
|
||||||
|
if(bezier.length<end[0]+1){for(i=_j=0,_ref=end[0];0<=_ref?_j<=_ref:_j>=_ref;i=0<=_ref?++_j:--_j){if(!(bezier[i]!=null)){leftCoord=[i-1,bezier[i-1]];for(j=_k=i,_ref1=end[0];i<=_ref1?_k<=_ref1:_k>=_ref1;j=i<=_ref1?++_k:--_k){if(bezier[j]!=null){rightCoord=[j,bezier[j]];break;}}
|
||||||
|
bezier[i]=leftCoord[1]+((rightCoord[1]-leftCoord[1])/(rightCoord[0]-leftCoord[0]))*(i-leftCoord[0]);}}}
|
||||||
|
if(!(bezier[end[0]]!=null)){bezier[end[0]]=bezier[end[0]-1];}
|
||||||
|
return bezier;};return Calculate;})();Convert=(function(){function Convert(){}
|
||||||
|
Convert.hexToRGB=function(hex){var b,g,r;if(hex.charAt(0)==="#"){hex=hex.substr(1);}
|
||||||
|
r=parseInt(hex.substr(0,2),16);g=parseInt(hex.substr(2,2),16);b=parseInt(hex.substr(4,2),16);return{r:r,g:g,b:b};};Convert.rgbToHSL=function(r,g,b){var d,h,l,max,min,s;if(typeof r==="object"){g=r.g;b=r.b;r=r.r;}
|
||||||
|
r/=255;g/=255;b/=255;max=Math.max(r,g,b);min=Math.min(r,g,b);l=(max+min)/2;if(max===min){h=s=0;}else{d=max-min;s=l>0.5?d/(2-max-min):d/(max+min);h=(function(){switch(max){case r:return(g-b)/d+(g<b?6:0);case g:return(b-r)/d+2;case b:return(r-g)/d+4;}})();h/=6;}
|
||||||
|
return{h:h,s:s,l:l};};Convert.hslToRGB=function(h,s,l){var b,g,p,q,r;if(typeof h==="object"){s=h.s;l=h.l;h=h.h;}
|
||||||
|
if(s===0){r=g=b=l;}else{q=l<0.5?l*(1+s):l+s-l*s;p=2*l-q;r=this.hueToRGB(p,q,h+1/3);g=this.hueToRGB(p,q,h);b=this.hueToRGB(p,q,h-1/3);}
|
||||||
|
return{r:r*255,g:g*255,b:b*255};};Convert.hueToRGB=function(p,q,t){if(t<0){t+=1;}
|
||||||
|
if(t>1){t-=1;}
|
||||||
|
if(t<1/6){return p+(q-p)*6*t;}
|
||||||
|
if(t<1/2){return q;}
|
||||||
|
if(t<2/3){return p+(q-p)*(2/3-t)*6;}
|
||||||
|
return p;};Convert.rgbToHSV=function(r,g,b){var d,h,max,min,s,v;r/=255;g/=255;b/=255;max=Math.max(r,g,b);min=Math.min(r,g,b);v=max;d=max-min;s=max===0?0:d/max;if(max===min){h=0;}else{h=(function(){switch(max){case r:return(g-b)/d+(g<b?6:0);case g:return(b-r)/d+2;case b:return(r-g)/d+4;}})();h/=6;}
|
||||||
|
return{h:h,s:s,v:v};};Convert.hsvToRGB=function(h,s,v){var b,f,g,i,p,q,r,t;i=Math.floor(h*6);f=h*6-i;p=v*(1-s);q=v*(1-f*s);t=v*(1-(1-f)*s);switch(i%6){case 0:r=v;g=t;b=p;break;case 1:r=q;g=v;b=p;break;case 2:r=p;g=v;b=t;break;case 3:r=p;g=q;b=v;break;case 4:r=t;g=p;b=v;break;case 5:r=v;g=p;b=q;}
|
||||||
|
return{r:r*255,g:g*255,b:b*255};};Convert.rgbToXYZ=function(r,g,b){var x,y,z;r/=255;g/=255;b/=255;if(r>0.04045){r=Math.pow((r+0.055)/1.055,2.4);}else{r/=12.92;}
|
||||||
|
if(g>0.04045){g=Math.pow((g+0.055)/1.055,2.4);}else{g/=12.92;}
|
||||||
|
if(b>0.04045){b=Math.pow((b+0.055)/1.055,2.4);}else{b/=12.92;}
|
||||||
|
x=r*0.4124+g*0.3576+b*0.1805;y=r*0.2126+g*0.7152+b*0.0722;z=r*0.0193+g*0.1192+b*0.9505;return{x:x*100,y:y*100,z:z*100};};Convert.xyzToRGB=function(x,y,z){var b,g,r;x/=100;y/=100;z/=100;r=(3.2406*x)+(-1.5372*y)+(-0.4986*z);g=(-0.9689*x)+(1.8758*y)+(0.0415*z);b=(0.0557*x)+(-0.2040*y)+(1.0570*z);if(r>0.0031308){r=(1.055*Math.pow(r,0.4166666667))-0.055;}else{r*=12.92;}
|
||||||
|
if(g>0.0031308){g=(1.055*Math.pow(g,0.4166666667))-0.055;}else{g*=12.92;}
|
||||||
|
if(b>0.0031308){b=(1.055*Math.pow(b,0.4166666667))-0.055;}else{b*=12.92;}
|
||||||
|
return{r:r*255,g:g*255,b:b*255};};Convert.xyzToLab=function(x,y,z){var a,b,l,whiteX,whiteY,whiteZ;if(typeof x==="object"){y=x.y;z=x.z;x=x.x;}
|
||||||
|
whiteX=95.047;whiteY=100.0;whiteZ=108.883;x/=whiteX;y/=whiteY;z/=whiteZ;if(x>0.008856451679){x=Math.pow(x,0.3333333333);}else{x=(7.787037037*x)+0.1379310345;}
|
||||||
|
if(y>0.008856451679){y=Math.pow(y,0.3333333333);}else{y=(7.787037037*y)+0.1379310345;}
|
||||||
|
if(z>0.008856451679){z=Math.pow(z,0.3333333333);}else{z=(7.787037037*z)+0.1379310345;}
|
||||||
|
l=116*y-16;a=500*(x-y);b=200*(y-z);return{l:l,a:a,b:b};};Convert.labToXYZ=function(l,a,b){var x,y,z;if(typeof l==="object"){a=l.a;b=l.b;l=l.l;}
|
||||||
|
y=(l+16)/116;x=y+(a/500);z=y-(b/200);if(x>0.2068965517){x=x*x*x;}else{x=0.1284185493*(x-0.1379310345);}
|
||||||
|
if(y>0.2068965517){y=y*y*y;}else{y=0.1284185493*(y-0.1379310345);}
|
||||||
|
if(z>0.2068965517){z=z*z*z;}else{z=0.1284185493*(z-0.1379310345);}
|
||||||
|
return{x:x*95.047,y:y*100.0,z:z*108.883};};Convert.rgbToLab=function(r,g,b){var xyz;if(typeof r==="object"){g=r.g;b=r.b;r=r.r;}
|
||||||
|
xyz=this.rgbToXYZ(r,g,b);return this.xyzToLab(xyz);};Convert.labToRGB=function(l,a,b){};return Convert;})();Event=(function(){function Event(){}
|
||||||
|
Event.events={};Event.types=["processStart","processComplete","renderFinished","blockStarted","blockFinished"];Event.trigger=function(target,type,data){var event,_i,_len,_ref,_results;if(this.events[type]&&this.events[type].length){_ref=this.events[type];_results=[];for(_i=0,_len=_ref.length;_i<_len;_i++){event=_ref[_i];if(event.target===null||target.id===event.target.id){_results.push(event.fn.call(target,data));}else{_results.push(void 0);}}
|
||||||
|
return _results;}};Event.listen=function(target,type,fn){var _fn,_type;if(typeof target==="string"){_type=target;_fn=type;target=null;type=_type;fn=_fn;}
|
||||||
|
if(__indexOf.call(this.types,type)<0){return false;}
|
||||||
|
if(!this.events[type]){this.events[type]=[];}
|
||||||
|
this.events[type].push({target:target,fn:fn});return true;};return Event;})();Caman.Event=Event;Filter=(function(){function Filter(){}
|
||||||
|
Filter.Type={Single:1,Kernel:2,LayerDequeue:3,LayerFinished:4,LoadOverlay:5,Plugin:6};Filter.register=function(name,filterFunc){return CamanInstance.prototype[name]=filterFunc;};Filter.prototype.render=function(callback){var _this=this;if(callback==null){callback=function(){};}
|
||||||
|
return this.processNext(function(){_this.context.putImageData(_this.imageData,0,0);return callback.call(_this);});};Filter.prototype.revert=function(ready){var i,pixel,_i,_len,_ref;if(ready==null){ready=function(){};}
|
||||||
|
_ref=this.originalPixelData;for(i=_i=0,_len=_ref.length;_i<_len;i=++_i){pixel=_ref[i];this.pixelData[i]=pixel;}
|
||||||
|
this.context.putImageData(this.imageData,0,0);return ready.call(this);};Filter.prototype.process=function(name,processFn){this.renderQueue.push({type:Filter.Type.Single,name:name,processFn:processFn});return this;};Filter.prototype.processKernel=function(name,adjust,divisor,bias){var i,_i,_ref;if(!divisor){divisor=0;for(i=_i=0,_ref=adjust.length;0<=_ref?_i<_ref:_i>_ref;i=0<=_ref?++_i:--_i){divisor+=adjust[i];}}
|
||||||
|
this.renderQueue.push({type:Filter.Type.Kernel,name:name,adjust:adjust,divisor:divisor,bias:bias||0});return this;};Filter.prototype.processPlugin=function(plugin,args){this.renderQueue.push({type:Filter.Type.Plugin,plugin:plugin,args:args});return this;};Filter.prototype.processNext=function(finishedFn){var next,_this=this;if(typeof finishedFn==="function"){this.finishedFn=finishedFn;}
|
||||||
|
if(this.renderQueue.length===0){if(this.finishedFn!=null){Event.trigger(this,"renderFinished");this.finishedFn.call(this);}
|
||||||
|
return this;}
|
||||||
|
next=this.renderQueue.shift();return RenderJob.execute(this,next,function(){return _this.processNext();});};Filter.prototype.newLayer=function(callback){var layer;layer=new Layer(this);this.canvasQueue.push(layer);this.renderQueue.push({type:Filter.Type.LayerDequeue});callback.call(layer);this.renderQueue.push({type:Filter.Type.LayerFinished});return this;};Filter.prototype.executeLayer=function(layer){this.pushContext(layer);return this.processNext();};Filter.prototype.pushContext=function(layer){this.layerStack.push(this.currentLayer);this.pixelStack.push(this.pixelData);this.currentLayer=layer;return this.pixelData=layer.pixelData;};Filter.prototype.popContext=function(){this.pixelData=this.pixelStack.pop();return this.currentLayer=this.layerStack.pop();};Filter.prototype.applyCurrentLayer=function(){return this.currentLayer.applyToParent();};return Filter;})();Util.extend(CamanInstance.prototype,Filter.prototype);Caman.Filter=Filter;IO=(function(){function IO(){}
|
||||||
|
IO.domainRegex=/(?:(?:http|https):\/\/)((?:\w+)\.(?:(?:\w|\.)+))/;IO.isRemote=function(url){var matches;if(!url){return;}
|
||||||
|
matches=url.match(this.domainRegex);if(matches){return matches[1]!==document.domain;}else{return false;}};IO.remoteCheck=function(src){if(this.isRemote(src)){if(!Caman.remoteProxy.length){Log.info("Attempting to load a remote image without a configured proxy. URL: "+src);}else{if(this.isRemote(Caman.remoteProxy)){Log.info("Cannot use a remote proxy for loading images.");return;}
|
||||||
|
return""+Caman.remoteProxy+"?camanProxyUrl="+(encodeURIComponent(src));}}};IO.useProxy=function(lang){var langToExt;langToExt={ruby:'rb',python:'py',perl:'pl',javascript:'js'};lang=lang.toLowerCase();if(langToExt[lang]!=null){lang=langToExt[lang];}
|
||||||
|
return"proxies/caman_proxy."+lang;};IO.prototype.save=function(){if(typeof exports!=="undefined"&&exports!==null){return this.nodeSave.apply(this,arguments);}else{return this.browserSave.apply(this,arguments);}};IO.prototype.browserSave=function(type){var image;if(type==null){type="png";}
|
||||||
|
type=type.toLowerCase();image=this.toBase64(type).replace("image/"+type,"image/octet-stream");return document.location.href=image;};IO.prototype.nodeSave=function(file,overwrite){var stats;if(overwrite==null){overwrite=true;}
|
||||||
|
try{stats=fs.statSync(file);if(stats.isFile()&&!overwrite){return false;}}catch(e){Log.debug("Creating output file "+file);}
|
||||||
|
return fs.writeFile(file,this.canvas.toBuffer(),function(){return Log.debug("Finished writing to "+file);});};IO.prototype.toImage=function(type){var img;img=document.createElement('img');img.src=this.toBase64(type);img.width=this.dimensions.width;img.height=this.dimensions.height;if(window.devicePixelRatio){img.width/=window.devicePixelRatio;img.height/=window.devicePixelRatio;}
|
||||||
|
return img;};IO.prototype.toBase64=function(type){if(type==null){type="png";}
|
||||||
|
type=type.toLowerCase();return this.canvas.toDataURL("image/"+type);};return IO;})();Util.extend(CamanInstance.prototype,IO.prototype);Caman.IO=IO;Layer=(function(){function Layer(c){this.c=c;this.filter=this.c;this.options={blendingMode:'normal',opacity:1.0};this.layerID=Util.uniqid.get();this.canvas=typeof exports!=="undefined"&&exports!==null?new Canvas():document.createElement('canvas');this.canvas.width=this.c.dimensions.width;this.canvas.height=this.c.dimensions.height;this.context=this.canvas.getContext('2d');this.context.createImageData(this.canvas.width,this.canvas.height);this.imageData=this.context.getImageData(0,0,this.canvas.width,this.canvas.height);this.pixelData=this.imageData.data;}
|
||||||
|
Layer.prototype.newLayer=function(cb){return this.c.newLayer.call(this.c,cb);};Layer.prototype.setBlendingMode=function(mode){this.options.blendingMode=mode;return this;};Layer.prototype.opacity=function(opacity){this.options.opacity=opacity/100;return this;};Layer.prototype.copyParent=function(){var i,parentData,_i,_ref;parentData=this.c.pixelData;for(i=_i=0,_ref=this.c.pixelData.length;_i<_ref;i=_i+=4){this.pixelData[i]=parentData[i];this.pixelData[i+1]=parentData[i+1];this.pixelData[i+2]=parentData[i+2];this.pixelData[i+3]=parentData[i+3];}
|
||||||
|
return this;};Layer.prototype.fillColor=function(){return this.c.fillColor.apply(this.c,arguments);};Layer.prototype.overlayImage=function(image){if(typeof image==="object"){image=image.src;}else if(typeof image==="string"&&image[0]==="#"){image=$(image).src;}
|
||||||
|
if(!image){return this;}
|
||||||
|
this.c.renderQueue.push({type:Filter.Type.LoadOverlay,src:image,layer:this});return this;};Layer.prototype.applyToParent=function(){var i,layerData,parentData,result,rgbaLayer,rgbaParent,_i,_ref,_results;parentData=this.c.pixelStack[this.c.pixelStack.length-1];layerData=this.c.pixelData;_results=[];for(i=_i=0,_ref=layerData.length;_i<_ref;i=_i+=4){rgbaParent={r:parentData[i],g:parentData[i+1],b:parentData[i+2],a:parentData[i+3]};rgbaLayer={r:layerData[i],g:layerData[i+1],b:layerData[i+2],a:layerData[i+3]};result=Blender.execute(this.options.blendingMode,rgbaLayer,rgbaParent);result.r=Util.clampRGB(result.r);result.g=Util.clampRGB(result.g);result.b=Util.clampRGB(result.b);if(!(result.a!=null)){result.a=rgbaLayer.a;}
|
||||||
|
parentData[i]=rgbaParent.r-((rgbaParent.r-result.r)*(this.options.opacity*(result.a/255)));parentData[i+1]=rgbaParent.g-((rgbaParent.g-result.g)*(this.options.opacity*(result.a/255)));_results.push(parentData[i+2]=rgbaParent.b-((rgbaParent.b-result.b)*(this.options.opacity*(result.a/255))));}
|
||||||
|
return _results;};return Layer;})();Logger=(function(){function Logger(){var name,_i,_len,_ref;_ref=['log','info','warn','error'];for(_i=0,_len=_ref.length;_i<_len;_i++){name=_ref[_i];this[name]=(function(name){return function(){if(!Caman.DEBUG){return;}
|
||||||
|
return console[name].apply(console,arguments);};})(name);}
|
||||||
|
this.debug=this.log;}
|
||||||
|
return Logger;})();Log=new Logger();PixelInfo=(function(){function PixelInfo(c){this.c=c;this.loc=0;}
|
||||||
|
PixelInfo.prototype.locationXY=function(){var x,y;y=this.c.dimensions.height-Math.floor(this.loc/(this.c.dimensions.width*4));x=(this.loc%(this.c.dimensions.width*4))/4;return{x:x,y:y};};PixelInfo.prototype.getPixelRelative=function(horiz,vert){var newLoc;newLoc=this.loc+(this.c.dimensions.width*4*(vert*-1))+(4*horiz);if(newLoc>this.c.pixelData.length||newLoc<0){return{r:0,g:0,b:0,a:0};}
|
||||||
|
return{r:this.c.pixelData[newLoc],g:this.c.pixelData[newLoc+1],b:this.c.pixelData[newLoc+2],a:this.c.pixelData[newLoc+3]};};PixelInfo.prototype.putPixelRelative=function(horiz,vert,rgba){var nowLoc;nowLoc=this.loc+(this.c.dimensions.width*4*(vert*-1))+(4*horiz);if(newLoc>this.c.pixelData.length||newLoc<0){return;}
|
||||||
|
this.c.pixelData[newLoc]=rgba.r;this.c.pixelData[newLoc+1]=rgba.g;this.c.pixelData[newLoc+2]=rgba.b;this.c.pixelData[newLoc+3]=rgba.a;return true;};PixelInfo.prototype.getPixel=function(x,y){var loc;loc=(y*this.c.dimensions.width+x)*4;return{r:this.c.pixelData[loc],g:this.c.pixelData[loc+1],b:this.c.pixelData[loc+2],a:this.c.pixelData[loc+3]};};PixelInfo.prototype.putPixel=function(x,y,rgba){var loc;loc=(y*this.c.dimensions.width+x)*4;this.c.pixelData[loc]=rgba.r;this.c.pixelData[loc+1]=rgba.g;this.c.pixelData[loc+2]=rgba.b;return this.c.pixelData[loc+3]=rgba.a;};return PixelInfo;})();Plugin=(function(){function Plugin(){}
|
||||||
|
Plugin.plugins={};Plugin.register=function(name,plugin){return this.plugins[name]=plugin;};Plugin.execute=function(context,name,args){return this.plugins[name].apply(context,args);};return Plugin;})();Caman.Plugin=Plugin;RenderJob=(function(){RenderJob.Blocks=4;RenderJob.execute=function(instance,job,callback){var layer,rj;rj=new RenderJob(instance,job,callback);switch(job.type){case Filter.Type.LayerDequeue:layer=instance.canvasQueue.shift();instance.executeLayer(layer);break;case Filter.Type.LayerFinished:instance.applyCurrentLayer();instance.popContext();callback();break;case Filter.Type.LoadOverlay:rj.loadOverlay(job.layer,job.src);break;case Filter.Type.Plugin:rj.executePlugin();break;default:rj.executeFilter();}
|
||||||
|
return instance;};function RenderJob(c,job,renderDone){this.c=c;this.job=job;this.renderDone=renderDone;}
|
||||||
|
RenderJob.prototype.executeFilter=function(){var blockN,blockPixelLength,end,j,lastBlockN,n,start,_i,_ref,_results,_this=this;this.blocksDone=0;n=this.c.pixelData.length;blockPixelLength=Math.floor((n/4)/RenderJob.Blocks);blockN=blockPixelLength*4;lastBlockN=blockN+((n/4)%RenderJob.Blocks)*4;Event.trigger(this.c,"processStart",this.job);if(this.job.type===Filter.Type.Single){_results=[];for(j=_i=0,_ref=RenderJob.Blocks;0<=_ref?_i<_ref:_i>_ref;j=0<=_ref?++_i:--_i){start=j*blockN;end=start+(j===RenderJob.Blocks-1?lastBlockN:blockN);_results.push(setTimeout((function(j,start,end){return function(){return _this.renderBlock(j,start,end);};})(j,start,end),0));}
|
||||||
|
return _results;}else{return this.renderKernel();}};RenderJob.prototype.executePlugin=function(){Log.debug("Executing plugin "+this.job.plugin);Plugin.execute(this.c,this.job.plugin,this.job.args);Log.debug("Plugin "+this.job.plugin+" finished!");return this.renderDone();};RenderJob.prototype.renderBlock=function(bnum,start,end){var data,i,pixelInfo,res,_i;Log.debug("BLOCK #"+bnum+" - Filter: "+this.job.name+", Start: "+start+", End: "+end);Event.trigger(this.c,"blockStarted",{blockNum:bnum,totalBlocks:RenderJob.Blocks,startPixel:start,endPixel:end});data={r:0,g:0,b:0,a:0};pixelInfo=new PixelInfo(this.c);for(i=_i=start;_i<end;i=_i+=4){pixelInfo.loc=i;data.r=this.c.pixelData[i];data.g=this.c.pixelData[i+1];data.b=this.c.pixelData[i+2];data.a=this.c.pixelData[i+3];res=this.job.processFn.call(pixelInfo,data);if(!(res.a!=null)){res.a=data.a;}
|
||||||
|
this.c.pixelData[i]=Util.clampRGB(res.r);this.c.pixelData[i+1]=Util.clampRGB(res.g);this.c.pixelData[i+2]=Util.clampRGB(res.b);this.c.pixelData[i+3]=Util.clampRGB(res.a);}
|
||||||
|
return this.blockFinished(bnum);};RenderJob.prototype.renderKernel=function(){var adjust,adjustSize,bias,builder,builderIndex,divisor,end,i,j,k,kernel,modPixelData,n,name,pixel,pixelInfo,res,start,_i,_j,_k,_l;name=this.job.name;bias=this.job.bias;divisor=this.job.divisor;n=this.c.pixelData.length;adjust=this.job.adjust;adjustSize=Math.sqrt(adjust.length);kernel=[];modPixelData=[];Log.debug("Rendering kernel - Filter: "+this.job.name);start=this.c.dimensions.width*4*((adjustSize-1)/2);end=n-(this.c.dimensions.width*4*((adjustSize-1)/2));builder=(adjustSize-1)/2;pixelInfo=new PixelInfo(this.c);for(i=_i=start;_i<end;i=_i+=4){pixelInfo.loc=i;builderIndex=0;for(j=_j=-builder;-builder<=builder?_j<=builder:_j>=builder;j=-builder<=builder?++_j:--_j){for(k=_k=builder;builder<=-builder?_k<=-builder:_k>=-builder;k=builder<=-builder?++_k:--_k){pixel=pixelInfo.getPixelRelative(j,k);kernel[builderIndex*3]=pixel.r;kernel[builderIndex*3+1]=pixel.g;kernel[builderIndex*3+2]=pixel.b;builderIndex++;}}
|
||||||
|
res=this.processKernel(adjust,kernel,divisor,bias);modPixelData[i]=Util.clampRGB(res.r);modPixelData[i+1]=Util.clampRGB(res.g);modPixelData[i+2]=Util.clampRGB(res.b);modPixelData[i+3]=this.c.pixelData[i+3];}
|
||||||
|
for(i=_l=start;start<=end?_l<end:_l>end;i=start<=end?++_l:--_l){this.c.pixelData[i]=modPixelData[i];}
|
||||||
|
return this.blockFinished(-1);};RenderJob.prototype.blockFinished=function(bnum){if(bnum>=0){Log.debug("Block #"+bnum+" finished! Filter: "+this.job.name);}
|
||||||
|
this.blocksDone++;Event.trigger(this.c,"blockFinished",{blockNum:bnum,blocksFinished:this.blocksDone,totalBlocks:RenderJob.Blocks});if(this.blocksDone===RenderJob.Blocks||bnum===-1){if(bnum>=0){Log.debug("Filter "+this.job.name+" finished!");}
|
||||||
|
if(bnum<0){Log.debug("Kernel filter "+this.job.name+" finished!");}
|
||||||
|
Event.trigger(this.c,"processComplete",this.job);return this.renderDone();}};RenderJob.prototype.processKernel=function(adjust,kernel,divisor,bias){var i,val,_i,_ref;val={r:0,g:0,b:0};for(i=_i=0,_ref=adjust.length;0<=_ref?_i<_ref:_i>_ref;i=0<=_ref?++_i:--_i){val.r+=adjust[i]*kernel[i*3];val.g+=adjust[i]*kernel[i*3+1];val.b+=adjust[i]*kernel[i*3+2];}
|
||||||
|
val.r=(val.r/divisor)+bias;val.g=(val.g/divisor)+bias;val.b=(val.b/divisor)+bias;return val;};RenderJob.prototype.loadOverlay=function(layer,src){var img,proxyUrl,_this=this;img=document.createElement('img');img.onload=function(){layer.context.drawImage(img,0,0,_this.c.dimensions.width,_this.c.dimensions.height);layer.imageData=layer.context.getImageData(0,0,_this.c.dimensions.width,_this.c.dimensions.height);layer.pixelData=layer.imageData.data;_this.c.pixelData=layer.pixelData;return _this.c.processNext();};proxyUrl=IO.remoteCheck(src);return img.src=proxyUrl!=null?proxyUrl:src;};return RenderJob;})();Store=(function(){function Store(){}
|
||||||
|
Store.items={};Store.has=function(search){return this.items[search]!=null;};Store.get=function(search){return this.items[search];};Store.put=function(name,obj){return this.items[name]=obj;};Store.execute=function(search,callback){return callback.call(this.get(search),this.get(search));};Store.flush=function(name){if(name==null){name=false;}
|
||||||
|
if(name){return delete this.items[name];}else{return this.items={};}};return Store;})();Caman.Store=Store;Blender.register("normal",function(rgbaLayer,rgbaParent){return{r:rgbaLayer.r,g:rgbaLayer.g,b:rgbaLayer.b};});Blender.register("multiply",function(rgbaLayer,rgbaParent){return{r:(rgbaLayer.r*rgbaParent.r)/255,g:(rgbaLayer.g*rgbaParent.g)/255,b:(rgbaLayer.b*rgbaParent.b)/255};});Blender.register("screen",function(rgbaLayer,rgbaParent){return{r:255-(((255-rgbaLayer.r)*(255-rgbaParent.r))/255),g:255-(((255-rgbaLayer.g)*(255-rgbaParent.g))/255),b:255-(((255-rgbaLayer.b)*(255-rgbaParent.b))/255)};});Blender.register("overlay",function(rgbaLayer,rgbaParent){var result;result={};result.r=rgbaParent.r>128?255-2*(255-rgbaLayer.r)*(255-rgbaParent.r)/255:(rgbaParent.r*rgbaLayer.r*2)/255;result.g=rgbaParent.g>128?255-2*(255-rgbaLayer.g)*(255-rgbaParent.g)/255:(rgbaParent.g*rgbaLayer.g*2)/255;result.b=rgbaParent.b>128?255-2*(255-rgbaLayer.b)*(255-rgbaParent.b)/255:(rgbaParent.b*rgbaLayer.b*2)/255;return result;});Blender.register("difference",function(rgbaLayer,rgbaParent){return{r:rgbaLayer.r-rgbaParent.r,g:rgbaLayer.g-rgbaParent.g,b:rgbaLayer.b-rgbaParent.b};});Blender.register("addition",function(rgbaLayer,rgbaParent){return{r:rgbaParent.r+rgbaLayer.r,g:rgbaParent.g+rgbaLayer.g,b:rgbaParent.b+rgbaLayer.b};});Blender.register("exclusion",function(rgbaLayer,rgbaParent){return{r:128-2*(rgbaParent.r-128)*(rgbaLayer.r-128)/255,g:128-2*(rgbaParent.g-128)*(rgbaLayer.g-128)/255,b:128-2*(rgbaParent.b-128)*(rgbaLayer.b-128)/255};});Blender.register("softLight",function(rgbaLayer,rgbaParent){var result;result={};result.r=rgbaParent.r>128?255-((255-rgbaParent.r)*(255-(rgbaLayer.r-128)))/255:(rgbaParent.r*(rgbaLayer.r+128))/255;result.g=rgbaParent.g>128?255-((255-rgbaParent.g)*(255-(rgbaLayer.g-128)))/255:(rgbaParent.g*(rgbaLayer.g+128))/255;result.b=rgbaParent.b>128?255-((255-rgbaParent.b)*(255-(rgbaLayer.b-128)))/255:(rgbaParent.b*(rgbaLayer.b+128))/255;return result;});Blender.register("lighten",function(rgbaLayer,rgbaParent){return{r:rgbaParent.r>rgbaLayer.r?rgbaParent.r:rgbaLayer.r,g:rgbaParent.g>rgbaLayer.g?rgbaParent.g:rgbaLayer.g,b:rgbaParent.b>rgbaLayer.b?rgbaParent.b:rgbaLayer.b};});Blender.register("darken",function(rgbaLayer,rgbaParent){return{r:rgbaParent.r>rgbaLayer.r?rgbaLayer.r:rgbaParent.r,g:rgbaParent.g>rgbaLayer.g?rgbaLayer.g:rgbaParent.g,b:rgbaParent.b>rgbaLayer.b?rgbaLayer.b:rgbaParent.b};});Filter.register("fillColor",function(){var color;if(arguments.length===1){color=Convert.hexToRGB(arguments[0]);}else{color={r:arguments[0],g:arguments[1],b:arguments[2]};}
|
||||||
|
return this.process("fillColor",function(rgba){rgba.r=color.r;rgba.g=color.g;rgba.b=color.b;rgba.a=255;return rgba;});});Filter.register("brightness",function(adjust){adjust=Math.floor(255*(adjust/100));return this.process("brightness",function(rgba){rgba.r+=adjust;rgba.g+=adjust;rgba.b+=adjust;return rgba;});});Filter.register("saturation",function(adjust){adjust*=-0.01;return this.process("saturation",function(rgba){var max;max=Math.max(rgba.r,rgba.g,rgba.b);if(rgba.r!==max){rgba.r+=(max-rgba.r)*adjust;}
|
||||||
|
if(rgba.g!==max){rgba.g+=(max-rgba.g)*adjust;}
|
||||||
|
if(rgba.b!==max){rgba.b+=(max-rgba.b)*adjust;}
|
||||||
|
return rgba;});});Filter.register("vibrance",function(adjust){adjust*=-1;return this.process("vibrance",function(rgba){var amt,avg,max;max=Math.max(rgba.r,rgba.g,rgba.b);avg=(rgba.r+rgba.g+rgba.b)/3;amt=((Math.abs(max-avg)*2/255)*adjust)/100;if(rgba.r!==max){rgba.r+=(max-rgba.r)*amt;}
|
||||||
|
if(rgba.g!==max){rgba.g+=(max-rgba.g)*amt;}
|
||||||
|
if(rgba.b!==max){rgba.b+=(max-rgba.b)*amt;}
|
||||||
|
return rgba;});});Filter.register("greyscale",function(adjust){return this.process("greyscale",function(rgba){var avg;avg=0.3*rgba.r+0.59*rgba.g+0.11*rgba.b;rgba.r=avg;rgba.g=avg;rgba.b=avg;return rgba;});});Filter.register("contrast",function(adjust){adjust=Math.pow((adjust+100)/100,2);return this.process("contrast",function(rgba){rgba.r/=255;rgba.r-=0.5;rgba.r*=adjust;rgba.r+=0.5;rgba.r*=255;rgba.g/=255;rgba.g-=0.5;rgba.g*=adjust;rgba.g+=0.5;rgba.g*=255;rgba.b/=255;rgba.b-=0.5;rgba.b*=adjust;rgba.b+=0.5;rgba.b*=255;return rgba;});});Filter.register("hue",function(adjust){return this.process("hue",function(rgba){var h,hsv,rgb;hsv=Convert.rgbToHSV(rgba.r,rgba.g,rgba.b);h=hsv.h*100;h+=Math.abs(adjust);h=h%100;h/=100;hsv.h=h;rgb=Convert.hsvToRGB(hsv.h,hsv.s,hsv.v);rgb.a=rgba.a;return rgb;});});Filter.register("colorize",function(){var level,rgb;if(arguments.length===2){rgb=Convert.hexToRGB(arguments[0]);level=arguments[1];}else if(arguments.length===4){rgb={r:arguments[0],g:arguments[1],b:arguments[2]};level=arguments[3];}
|
||||||
|
return this.process("colorize",function(rgba){rgba.r-=(rgba.r-rgb.r)*(level/100);rgba.g-=(rgba.g-rgb.g)*(level/100);rgba.b-=(rgba.b-rgb.b)*(level/100);return rgba;});});Filter.register("invert",function(){return this.process("invert",function(rgba){rgba.r=255-rgba.r;rgba.g=255-rgba.g;rgba.b=255-rgba.b;return rgba;});});Filter.register("sepia",function(adjust){if(adjust==null){adjust=100;}
|
||||||
|
adjust/=100;return this.process("sepia",function(rgba){rgba.r=Math.min(255,(rgba.r*(1-(0.607*adjust)))+(rgba.g*(0.769*adjust))+(rgba.b*(0.189*adjust)));rgba.g=Math.min(255,(rgba.r*(0.349*adjust))+(rgba.g*(1-(0.314*adjust)))+(rgba.b*(0.168*adjust)));rgba.b=Math.min(255,(rgba.r*(0.272*adjust))+(rgba.g*(0.534*adjust))+(rgba.b*(1-(0.869*adjust))));return rgba;});});Filter.register("gamma",function(adjust){return this.process("gamma",function(rgba){rgba.r=Math.pow(rgba.r/255,adjust)*255;rgba.g=Math.pow(rgba.g/255,adjust)*255;rgba.b=Math.pow(rgba.b/255,adjust)*255;return rgba;});});Filter.register("noise",function(adjust){adjust=Math.abs(adjust)*2.55;return this.process("noise",function(rgba){var rand;rand=Calculate.randomRange(adjust*-1,adjust);rgba.r+=rand;rgba.g+=rand;rgba.b+=rand;return rgba;});});Filter.register("clip",function(adjust){adjust=Math.abs(adjust)*2.55;return this.process("clip",function(rgba){if(rgba.r>255-adjust){rgba.r=255;}else if(rgba.r<adjust){rgba.r=0;}
|
||||||
|
if(rgba.g>255-adjust){rgba.g=255;}else if(rgba.g<adjust){rgba.g=0;}
|
||||||
|
if(rgba.b>255-adjust){rgba.b=255;}else if(rgba.b<adjust){rgba.b=0;}
|
||||||
|
return rgba;});});Filter.register("channels",function(options){var chan,value;if(typeof options!=="object"){return this;}
|
||||||
|
for(chan in options){if(!__hasProp.call(options,chan))continue;value=options[chan];if(value===0){delete options[chan];continue;}
|
||||||
|
options[chan]/=100;}
|
||||||
|
if(options.length===0){return this;}
|
||||||
|
return this.process("channels",function(rgba){if(options.red!=null){if(options.red>0){rgba.r+=(255-rgba.r)*options.red;}else{rgba.r-=rgba.r*Math.abs(options.red);}}
|
||||||
|
if(options.green!=null){if(options.green>0){rgba.g+=(255-rgba.g)*options.green;}else{rgba.g-=rgba.g*Math.abs(options.green);}}
|
||||||
|
if(options.blue!=null){if(options.blue>0){rgba.b+=(255-rgba.b)*options.blue;}else{rgba.b-=rgba.b*Math.abs(options.blue);}}
|
||||||
|
return rgba;});});Filter.register("curves",function(){var bezier,chans,cps,ctrl1,ctrl2,end,i,start,_i,_j,_ref,_ref1;chans=arguments[0],cps=2<=arguments.length?__slice.call(arguments,1):[];if(typeof chans==="string"){chans=chans.split("");}
|
||||||
|
if(chans[0]==="v"){chans=['r','g','b'];}
|
||||||
|
if(cps.length<3||cps.length>4){throw"Invalid number of arguments to curves filter";}
|
||||||
|
start=cps[0];ctrl1=cps[1];ctrl2=cps.length===4?cps[2]:cps[1];end=cps[cps.length-1];bezier=Calculate.bezier(start,ctrl1,ctrl2,end,0,255);if(start[0]>0){for(i=_i=0,_ref=start[0];0<=_ref?_i<_ref:_i>_ref;i=0<=_ref?++_i:--_i){bezier[i]=start[1];}}
|
||||||
|
if(end[0]<255){for(i=_j=_ref1=end[0];_ref1<=255?_j<=255:_j>=255;i=_ref1<=255?++_j:--_j){bezier[i]=end[1];}}
|
||||||
|
return this.process("curves",function(rgba){var _k,_ref2;for(i=_k=0,_ref2=chans.length;0<=_ref2?_k<_ref2:_k>_ref2;i=0<=_ref2?++_k:--_k){rgba[chans[i]]=bezier[rgba[chans[i]]];}
|
||||||
|
return rgba;});});Filter.register("exposure",function(adjust){var ctrl1,ctrl2,p;p=Math.abs(adjust)/100;ctrl1=[0,255*p];ctrl2=[255-(255*p),255];if(adjust<0){ctrl1=ctrl1.reverse();ctrl2=ctrl2.reverse();}
|
||||||
|
return this.curves('rgb',[0,0],ctrl1,ctrl2,[255,255]);});Caman.Filter.register("boxBlur",function(){return this.processKernel("Box Blur",[1,1,1,1,1,1,1,1,1]);});Caman.Filter.register("radialBlur",function(){return this.processKernel("Radial Blur",[0,1,0,1,1,1,0,1,0]);});Caman.Filter.register("heavyRadialBlur",function(){return this.processKernel("Heavy Radial Blur",[0,0,1,0,0,0,1,1,1,0,1,1,1,1,1,0,1,1,1,0,0,0,1,0,0]);});Caman.Filter.register("gaussianBlur",function(){return this.processKernel("Gaussian Blur",[1,4,6,4,1,4,16,24,16,4,6,24,36,24,6,4,16,24,16,4,1,4,6,4,1]);});Caman.Filter.register("motionBlur",function(){var kernel;if(degrees===0||degrees===180){kernel=[0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1,0,0];}else if((degrees>0&°rees<90)||(degrees>180&°rees<270)){kernel=[0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0];}else if(degrees===90||degrees===270){kernel=[0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0];}else{kernel=[1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1];}
|
||||||
|
return this.processKernel("Motion Blur",kernel);});Caman.Filter.register("sharpen",function(amt){if(amt==null){amt=100;}
|
||||||
|
amt/=100;return this.processKernel("Sharpen",[0,-amt,0,-amt,4*amt+1,-amt,0,-amt,0]);});vignetteFilters={brightness:function(rgba,amt,opts){rgba.r=rgba.r-(rgba.r*amt*opts.strength);rgba.g=rgba.g-(rgba.g*amt*opts.strength);rgba.b=rgba.b-(rgba.b*amt*opts.strength);return rgba;},gamma:function(rgba,amt,opts){rgba.r=Math.pow(rgba.r/255,Math.max(10*amt*opts.strength,1))*255;rgba.g=Math.pow(rgba.g/255,Math.max(10*amt*opts.strength,1))*255;rgba.b=Math.pow(rgba.b/255,Math.max(10*amt*opts.strength,1))*255;return rgba;},colorize:function(rgba,amt,opts){rgba.r-=(rgba.r-opts.color.r)*amt;rgba.g-=(rgba.g-opts.color.g)*amt;rgba.b-=(rgba.b-opts.color.b)*amt;return rgba;}};Filter.register("vignette",function(size,strength){var bezier,center,end,start;if(strength==null){strength=60;}
|
||||||
|
if(typeof size==="string"&&size.substr(-1)==="%"){if(this.dimensions.height>this.dimensions.width){size=this.dimensions.width*(parseInt(size.substr(0,size.length-1),10)/100);}else{size=this.dimensions.height*(parseInt(size.substr(0,size.length-1),10)/100);}}
|
||||||
|
strength/=100;center=[this.dimensions.width/2,this.dimensions.height/2];start=Math.sqrt(Math.pow(center[0],2)+Math.pow(center[1],2));end=start-size;bezier=Calculate.bezier([0,1],[30,30],[70,60],[100,80]);return this.process("vignette",function(rgba){var dist,div,loc;loc=this.locationXY();dist=Calculate.distance(loc.x,loc.y,center[0],center[1]);if(dist>end){div=Math.max(1,(bezier[Math.round(((dist-end)/size)*100)]/10)*strength);rgba.r=Math.pow(rgba.r/255,div)*255;rgba.g=Math.pow(rgba.g/255,div)*255;rgba.b=Math.pow(rgba.b/255,div)*255;}
|
||||||
|
return rgba;});});Filter.register("rectangularVignette",function(opts){var defaults,dim,percent,size,_i,_len,_ref;defaults={strength:50,cornerRadius:0,method:'brightness',color:{r:0,g:0,b:0}};opts=Util.extend(defaults,opts);if(!opts.size){return this;}else if(typeof opts.size==="string"){percent=parseInt(opts.size,10)/100;opts.size={width:this.dimensions.width*percent,height:this.dimensions.height*percent};}else if(typeof opts.size==="object"){_ref=["width","height"];for(_i=0,_len=_ref.length;_i<_len;_i++){dim=_ref[_i];if(typeof opts.size[dim]==="string"){opts.size[dim]=this.dimensions[dim]*(parseInt(opts.size[dim],10)/100);}}}else if(opts.size==="number"){size=opts.size;opts.size={width:size,height:size};}
|
||||||
|
if(typeof opts.cornerRadius==="string"){opts.cornerRadius=(opts.size.width/2)*(parseInt(opts.cornerRadius,10)/100);}
|
||||||
|
opts.strength/=100;opts.size.width=Math.floor(opts.size.width);opts.size.height=Math.floor(opts.size.height);opts.image={width:this.dimensions.width,height:this.dimensions.height};if(opts.method==="colorize"&&typeof opts.color==="string"){opts.color=Convert.hexToRGB(opts.color);}
|
||||||
|
opts.coords={left:(this.dimensions.width-opts.size.width)/2,right:this.dimensions.width-opts.coords.left,bottom:(this.dimensions.height-opts.size.height)/2,top:this.dimensions.height-opts.coords.bottom};opts.corners=[{x:opts.coords.left+opts.cornerRadius,y:opts.coords.top-opts.cornerRadius},{x:opts.coords.right-opts.cornerRadius,y:opts.coords.top-opts.cornerRadius},{x:opts.coords.right-opts.cornerRadius,y:opts.coords.bottom+opts.cornerRadius},{x:opts.coords.left+opts.cornerRadius,y:opts.coords.bottom+opts.cornerRadius}];opts.maxDist=Calculate.distance(0,0,opts.corners[3].x,opts.corners[3].y)-opts.cornerRadius;return this.process("rectangularVignette",function(rgba){var amt,loc,radialDist;loc=this.locationXY();if((loc.x>opts.corners[0].x&&loc.x<opts.corners[1].x)&&(loc.y>opts.coords.bottom&&loc.y<opts.coords.top)){return rgba;}
|
||||||
|
if((loc.x>opts.coords.left&&loc.x<opts.coords.right)&&(loc.y>opts.corners[3].y&&loc.y<opts.corners[2].y)){return rgba;}
|
||||||
|
if(loc.x>opts.corners[0].x&&loc.x<opts.corners[1].x&&loc.y>opts.coords.top){amt=(loc.y-opts.coords.top)/opts.maxDist;}else if(loc.y>opts.corners[2].y&&loc.y<opts.corners[1].y&&loc.x>opts.coords.right){amt=(loc.x-opts.coords.right)/opts.maxDist;}else if(loc.x>opts.corners[0].x&&loc.x<opts.corners[1].x&&loc.y<opts.coords.bottom){amt=(opts.coords.bottom-loc.y)/opts.maxDist;}else if(loc.y>opts.corners[2].y&&loc.y<opts.corners[1].y&&loc.x<opts.coords.left){amt=(opts.coords.left-loc.x)/opts.maxDist;}else if(loc.x<=opts.corners[0].x&&loc.y>=opts.corners[0].y){radialDist=Caman.distance(loc.x,loc.y,opts.corners[0].x,opts.corners[0].y);amt=(radialDist-opts.cornerRadius)/opts.maxDist;}else if(loc.x>=opts.corners[1].x&&loc.y>=opts.corners[1].y){radialDist=Caman.distance(loc.x,loc.y,opts.corners[1].x,opts.corners[1].y);amt=(radialDist-opts.cornerRadius)/opts.maxDist;}else if(loc.x>=opts.corners[2].x&&loc.y<=opts.corners[2].y){radialDist=Caman.distance(loc.x,loc.y,opts.corners[2].x,opts.corners[2].y);amt=(radialDist-opts.cornerRadius)/opts.maxDist;}else if(loc.x<=opts.corners[3].x&&loc.y<=opts.corners[3].y){radialDist=Caman.distance(loc.x,loc.y,opts.corners[3].x,opts.corners[3].y);amt=(radialDist-opts.cornerRadius)/opts.maxDist;}
|
||||||
|
if(amt<0){return rgba;}
|
||||||
|
return vignetteFilters[opts.method](rgba,amt,opts);});});(function(){var BlurStack,getLinearGradientMap,getRadialGradientMap,mul_table,shg_table;mul_table=[512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,289,287,285,282,280,278,275,273,271,269,267,265,263,261,259];shg_table=[9,11,12,13,13,14,14,15,15,15,15,16,16,16,16,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24];getLinearGradientMap=function(width,height,centerX,centerY,angle,length,mirrored){var cnv,context,gradient,x1,x2,y1,y2;cnv=typeof exports!=="undefined"&&exports!==null?new Canvas():document.createElement('canvas');cnv.width=width;cnv.height=height;x1=centerX+Math.cos(angle)*length*0.5;y1=centerY+Math.sin(angle)*length*0.5;x2=centerX-Math.cos(angle)*length*0.5;y2=centerY-Math.sin(angle)*length*0.5;context=cnv.getContext("2d");gradient=context.createLinearGradient(x1,y1,x2,y2);if(!mirrored){gradient.addColorStop(0,"white");gradient.addColorStop(1,"black");}else{gradient.addColorStop(0,"white");gradient.addColorStop(0.5,"black");gradient.addColorStop(1,"white");}
|
||||||
|
context.fillStyle=gradient;context.fillRect(0,0,width,height);return context.getImageData(0,0,width,height);};getRadialGradientMap=function(width,height,centerX,centerY,radius1,radius2){var cnv,context,gradient;cnv=typeof exports!=="undefined"&&exports!==null?new Canvas():document.createElement('canvas');cnv.width=width;cnv.height=height;context=cnv.getContext("2d");gradient=context.createRadialGradient(centerX,centerY,radius1,centerX,centerY,radius2);gradient.addColorStop(1,"white");gradient.addColorStop(0,"black");context.fillStyle=gradient;context.fillRect(0,0,width,height);return context.getImageData(0,0,width,height);};BlurStack=function(){this.r=0;this.g=0;this.b=0;this.a=0;return this.next=null;};Caman.Plugin.register("compoundBlur",function(radiusData,radius,increaseFactor,blurLevels){var b_in_sum,b_out_sum,b_sum,blend,currentIndex,div,g_in_sum,g_out_sum,g_sum,height,heightMinus1,i,iblend,idx,imagePixels,index,iradius,lookupValue,mul_sum,p,pb,pg,pixels,pr,r_in_sum,r_out_sum,r_sum,radiusPixels,radiusPlus1,rbs,shg_sum,stack,stackEnd,stackIn,stackOut,stackStart,steps,sumFactor,w4,wh,wh4,width,widthMinus1,x,y,yi,yp,yw,_i,_j,_k,_l,_m,_n,_o,_p,_q,_r;width=this.dimensions.width;height=this.dimensions.height;imagePixels=this.pixelData;radiusPixels=radiusData.data;wh=width*height;wh4=wh<<2;pixels=[];for(i=_i=0;0<=wh4?_i<wh4:_i>wh4;i=0<=wh4?++_i:--_i){pixels[i]=imagePixels[i];}
|
||||||
|
currentIndex=0;steps=blurLevels;blurLevels-=1;while(steps-->=0){iradius=(radius+0.5)|0;if(iradius===0){continue;}
|
||||||
|
if(iradius>256){iradius=256;}
|
||||||
|
div=iradius+iradius+1;w4=width<<2;widthMinus1=width-1;heightMinus1=height-1;radiusPlus1=iradius+1;sumFactor=radiusPlus1*(radiusPlus1+1)/2;stackStart=new BlurStack();stackEnd=void 0;stack=stackStart;for(i=_j=1;1<=div?_j<div:_j>div;i=1<=div?++_j:--_j){stack=stack.next=new BlurStack();if(i===radiusPlus1){stackEnd=stack;}}
|
||||||
|
stack.next=stackStart;stackIn=null;stackOut=null;yw=yi=0;mul_sum=mul_table[iradius];shg_sum=shg_table[iradius];for(y=_k=0;0<=height?_k<height:_k>height;y=0<=height?++_k:--_k){r_in_sum=g_in_sum=b_in_sum=r_sum=g_sum=b_sum=0;r_out_sum=radiusPlus1*(pr=pixels[yi]);g_out_sum=radiusPlus1*(pg=pixels[yi+1]);b_out_sum=radiusPlus1*(pb=pixels[yi+2]);r_sum+=sumFactor*pr;g_sum+=sumFactor*pg;b_sum+=sumFactor*pb;stack=stackStart;for(i=_l=0;0<=radiusPlus1?_l<radiusPlus1:_l>radiusPlus1;i=0<=radiusPlus1?++_l:--_l){stack.r=pr;stack.g=pg;stack.b=pb;stack=stack.next;}
|
||||||
|
for(i=_m=1;1<=radiusPlus1?_m<radiusPlus1:_m>radiusPlus1;i=1<=radiusPlus1?++_m:--_m){p=yi+((widthMinus1<i?widthMinus1:i)<<2);r_sum+=(stack.r=(pr=pixels[p]))*(rbs=radiusPlus1-i);g_sum+=(stack.g=(pg=pixels[p+1]))*rbs;b_sum+=(stack.b=(pb=pixels[p+2]))*rbs;r_in_sum+=pr;g_in_sum+=pg;b_in_sum+=pb;stack=stack.next;}
|
||||||
|
stackIn=stackStart;stackOut=stackEnd;for(x=_n=0;0<=width?_n<width:_n>width;x=0<=width?++_n:--_n){pixels[yi]=(r_sum*mul_sum)>>shg_sum;pixels[yi+1]=(g_sum*mul_sum)>>shg_sum;pixels[yi+2]=(b_sum*mul_sum)>>shg_sum;r_sum-=r_out_sum;g_sum-=g_out_sum;b_sum-=b_out_sum;r_out_sum-=stackIn.r;g_out_sum-=stackIn.g;b_out_sum-=stackIn.b;p=(yw+((p=x+radiusPlus1)<widthMinus1?p:widthMinus1))<<2;r_in_sum+=(stackIn.r=pixels[p]);g_in_sum+=(stackIn.g=pixels[p+1]);b_in_sum+=(stackIn.b=pixels[p+2]);r_sum+=r_in_sum;g_sum+=g_in_sum;b_sum+=b_in_sum;stackIn=stackIn.next;r_out_sum+=(pr=stackOut.r);g_out_sum+=(pg=stackOut.g);b_out_sum+=(pb=stackOut.b);r_in_sum-=pr;g_in_sum-=pg;b_in_sum-=pb;stackOut=stackOut.next;yi+=4;}
|
||||||
|
yw+=width;}
|
||||||
|
for(x=_o=0;0<=width?_o<width:_o>width;x=0<=width?++_o:--_o){g_in_sum=b_in_sum=r_in_sum=g_sum=b_sum=r_sum=0;yi=x<<2;r_out_sum=radiusPlus1*(pr=pixels[yi]);g_out_sum=radiusPlus1*(pg=pixels[yi+1]);b_out_sum=radiusPlus1*(pb=pixels[yi+2]);r_sum+=sumFactor*pr;g_sum+=sumFactor*pg;b_sum+=sumFactor*pb;stack=stackStart;for(i=_p=0;0<=radiusPlus1?_p<radiusPlus1:_p>radiusPlus1;i=0<=radiusPlus1?++_p:--_p){stack.r=pr;stack.g=pg;stack.b=pb;stack=stack.next;}
|
||||||
|
yp=width;for(i=_q=1;1<=radiusPlus1?_q<radiusPlus1:_q>radiusPlus1;i=1<=radiusPlus1?++_q:--_q){yi=(yp+x)<<2;r_sum+=(stack.r=(pr=pixels[yi]))*(rbs=radiusPlus1-i);g_sum+=(stack.g=(pg=pixels[yi+1]))*rbs;b_sum+=(stack.b=(pb=pixels[yi+2]))*rbs;r_in_sum+=pr;g_in_sum+=pg;b_in_sum+=pb;stack=stack.next;if(i<heightMinus1){yp+=width;}}
|
||||||
|
yi=x;stackIn=stackStart;stackOut=stackEnd;for(y=_r=0;0<=height?_r<height:_r>height;y=0<=height?++_r:--_r){p=yi<<2;pixels[p]=(r_sum*mul_sum)>>shg_sum;pixels[p+1]=(g_sum*mul_sum)>>shg_sum;pixels[p+2]=(b_sum*mul_sum)>>shg_sum;r_sum-=r_out_sum;g_sum-=g_out_sum;b_sum-=b_out_sum;r_out_sum-=stackIn.r;g_out_sum-=stackIn.g;b_out_sum-=stackIn.b;p=(x+(((p=y+radiusPlus1)<heightMinus1?p:heightMinus1)*width))<<2;r_sum+=(r_in_sum+=(stackIn.r=pixels[p]));g_sum+=(g_in_sum+=(stackIn.g=pixels[p+1]));b_sum+=(b_in_sum+=(stackIn.b=pixels[p+2]));stackIn=stackIn.next;r_out_sum+=(pr=stackOut.r);g_out_sum+=(pg=stackOut.g);b_out_sum+=(pb=stackOut.b);r_in_sum-=pr;g_in_sum-=pg;b_in_sum-=pb;stackOut=stackOut.next;yi+=width;}}
|
||||||
|
radius*=increaseFactor;i=wh;while(--i>-1){idx=i<<2;lookupValue=(radiusPixels[idx+2]&0xff)/255.0*blurLevels;index=lookupValue|0;if(index===currentIndex){blend=256.0*(lookupValue-(lookupValue|0));iblend=256-blend;imagePixels[idx]=(imagePixels[idx]*iblend+pixels[idx]*blend)>>8;imagePixels[idx+1]=(imagePixels[idx+1]*iblend+pixels[idx+1]*blend)>>8;imagePixels[idx+2]=(imagePixels[idx+2]*iblend+pixels[idx+2]*blend)>>8;}else if(index===currentIndex+1){imagePixels[idx]=pixels[idx];imagePixels[idx+1]=pixels[idx+1];imagePixels[idx+2]=pixels[idx+2];}}
|
||||||
|
currentIndex++;}
|
||||||
|
return this;});Caman.Filter.register("tiltShift",function(opts){var defaults,gradient;defaults={center:{x:this.dimensions.width/2,y:this.dimensions.height/2},angle:45,focusWidth:200,startRadius:3,radiusFactor:1.5,steps:3};opts=Caman.Util.extend(defaults,opts);opts.angle*=Math.PI/180;gradient=getLinearGradientMap(this.dimensions.width,this.dimensions.height,opts.center.x,opts.center.y,opts.angle,opts.focusWidth,true);return this.processPlugin("compoundBlur",[gradient,opts.startRadius,opts.radiusFactor,opts.steps]);});return Caman.Filter.register("radialBlur",function(opts){var defaults,gradient,radius1,radius2;defaults={size:50,center:{x:this.dimensions.width/2,y:this.dimensions.height/2},startRadius:3,radiusFactor:1.5,steps:3,radius:null};opts=Caman.Util.extend(defaults,opts);if(!opts.radius){opts.radius=this.dimensions.width<this.dimensions.height?this.dimensions.height:this.dimensions.width;}
|
||||||
|
radius1=(opts.radius/2)-opts.size;radius2=opts.radius/2;gradient=getRadialGradientMap(this.dimensions.width,this.dimensions.height,opts.center.x,opts.center.y,radius1,radius2);return this.processPlugin("compoundBlur",[gradient,opts.startRadius,opts.radiusFactor,opts.steps]);});})();Caman.Filter.register("edgeEnhance",function(){return this.processKernel("Edge Enhance",[0,0,0,-1,1,0,0,0,0]);});Caman.Filter.register("edgeDetect",function(){return this.processKernel("Edge Detect",[-1,-1,-1,-1,8,-1,-1,-1,-1]);});Caman.Filter.register("emboss",function(){return this.processKernel("Emboss",[-2,-1,0,-1,1,1,0,1,2]);});Caman.Filter.register("posterize",function(adjust){var numOfAreas,numOfValues;numOfAreas=256/adjust;numOfValues=255/(adjust-1);return this.process("posterize",function(rgba){rgba.r=Math.floor(Math.floor(rgba.r/numOfAreas)*numOfValues);rgba.g=Math.floor(Math.floor(rgba.g/numOfAreas)*numOfValues);rgba.b=Math.floor(Math.floor(rgba.b/numOfAreas)*numOfValues);return rgba;});});Caman.Filter.register("vintage",function(vignette){if(vignette==null){vignette=true;}
|
||||||
|
this.greyscale();this.contrast(5);this.noise(3);this.sepia(100);this.channels({red:8,blue:2,green:4});this.gamma(0.87);if(vignette){return this.vignette("40%",30);}});Caman.Filter.register("lomo",function(vignette){if(vignette==null){vignette=true;}
|
||||||
|
this.brightness(15);this.exposure(15);this.curves('rgb',[0,0],[200,0],[155,255],[255,255]);this.saturation(-20);this.gamma(1.8);if(vignette){this.vignette("50%",60);}
|
||||||
|
return this.brightness(5);});Caman.Filter.register("clarity",function(grey){if(grey==null){grey=false;}
|
||||||
|
this.vibrance(20);this.curves('rgb',[5,0],[130,150],[190,220],[250,255]);this.sharpen(15);this.vignette("45%",20);if(grey){this.greyscale();this.contrast(4);}
|
||||||
|
return this;});Caman.Filter.register("sinCity",function(){this.contrast(100);this.brightness(15);this.exposure(10);this.posterize(80);this.clip(30);return this.greyscale();});Caman.Filter.register("sunrise",function(){this.exposure(3.5);this.saturation(-5);this.vibrance(50);this.sepia(60);this.colorize("#e87b22",10);this.channels({red:8,blue:8});this.contrast(5);this.gamma(1.2);return this.vignette("55%",25);});Caman.Filter.register("crossProcess",function(){this.exposure(5);this.colorize("#e87b22",4);this.sepia(20);this.channels({blue:8,red:3});this.curves('b',[0,0],[100,150],[180,180],[255,255]);this.contrast(15);this.vibrance(75);return this.gamma(1.6);});Caman.Filter.register("orangePeel",function(){this.curves('rgb',[0,0],[100,50],[140,200],[255,255]);this.vibrance(-30);this.saturation(-30);this.colorize('#ff9000',30);this.contrast(-5);return this.gamma(1.4);});Caman.Filter.register("love",function(){this.brightness(5);this.exposure(8);this.contrast(4);this.colorize('#c42007',30);this.vibrance(50);return this.gamma(1.3);});Caman.Filter.register("grungy",function(){this.gamma(1.5);this.clip(25);this.saturation(-60);this.contrast(5);this.noise(5);return this.vignette("50%",30);});Caman.Filter.register("jarques",function(){this.saturation(-35);this.curves('b',[20,0],[90,120],[186,144],[255,230]);this.curves('r',[0,0],[144,90],[138,120],[255,255]);this.curves('g',[10,0],[115,105],[148,100],[255,248]);this.curves('rgb',[0,0],[120,100],[128,140],[255,255]);return this.sharpen(20);});Caman.Filter.register("pinhole",function(){this.greyscale();this.sepia(10);this.exposure(10);this.contrast(15);return this.vignette("60%",35);});Caman.Filter.register("oldBoot",function(){this.saturation(-20);this.vibrance(-50);this.gamma(1.1);this.sepia(30);this.channels({red:-10,blue:5});this.curves('rgb',[0,0],[80,50],[128,230],[255,255]);return this.vignette("60%",30);});Caman.Filter.register("glowingSun",function(vignette){if(vignette==null){vignette=true;}
|
||||||
|
this.brightness(10);this.newLayer(function(){this.setBlendingMode("multiply");this.opacity(80);this.copyParent();this.filter.gamma(0.8);this.filter.contrast(50);return this.filter.exposure(10);});this.newLayer(function(){this.setBlendingMode("softLight");this.opacity(80);return this.fillColor("#f49600");});this.exposure(20);this.gamma(0.8);if(vignette){return this.vignette("45%",20);}});Caman.Filter.register("hazyDays",function(){this.gamma(1.2);this.newLayer(function(){this.setBlendingMode("overlay");this.opacity(60);this.copyParent();this.filter.channels({red:5});return this.filter.stackBlur(15);});this.newLayer(function(){this.setBlendingMode("addition");this.opacity(40);return this.fillColor("#6899ba");});this.newLayer(function(){this.setBlendingMode("multiply");this.opacity(35);this.copyParent();this.filter.brightness(40);this.filter.vibrance(40);this.filter.exposure(30);this.filter.contrast(15);this.filter.curves('r',[0,40],[128,128],[128,128],[255,215]);this.filter.curves('g',[0,40],[128,128],[128,128],[255,215]);this.filter.curves('b',[0,40],[128,128],[128,128],[255,215]);return this.filter.stackBlur(5);});this.curves('r',[20,0],[128,158],[128,128],[235,255]);this.curves('g',[20,0],[128,128],[128,128],[235,255]);this.curves('b',[20,0],[128,108],[128,128],[235,255]);return this.vignette("45%",20);});Caman.Filter.register("herMajesty",function(){this.brightness(40);this.colorize("#ea1c5d",10);this.curves('b',[0,10],[128,180],[190,190],[255,255]);this.newLayer(function(){this.setBlendingMode('overlay');this.opacity(50);this.copyParent();this.filter.gamma(0.7);return this.newLayer(function(){this.setBlendingMode('normal');this.opacity(60);return this.fillColor('#ea1c5d');});});this.newLayer(function(){this.setBlendingMode('multiply');this.opacity(60);this.copyParent();this.filter.saturation(50);this.filter.hue(90);return this.filter.contrast(10);});this.gamma(1.4);this.vibrance(-30);this.newLayer(function(){this.opacity(10);return this.fillColor('#e5f0ff');});return this;});Caman.Filter.register("nostalgia",function(){this.saturation(20);this.gamma(1.4);this.greyscale();this.contrast(5);this.sepia(100);this.channels({red:8,blue:2,green:4});this.gamma(0.8);this.contrast(5);this.exposure(10);this.newLayer(function(){this.setBlendingMode('overlay');this.copyParent();this.opacity(55);return this.filter.stackBlur(10);});return this.vignette("50%",30);});Caman.Filter.register("hemingway",function(){this.greyscale();this.contrast(10);this.gamma(0.9);this.newLayer(function(){this.setBlendingMode("multiply");this.opacity(40);this.copyParent();this.filter.exposure(15);this.filter.contrast(15);return this.filter.channels({green:10,red:5});});this.sepia(30);this.curves('rgb',[0,10],[120,90],[180,200],[235,255]);this.channels({red:5,green:-2});return this.exposure(15);});Caman.Filter.register("concentrate",function(){this.sharpen(40);this.saturation(-50);this.channels({red:3});this.newLayer(function(){this.setBlendingMode("multiply");this.opacity(80);this.copyParent();this.filter.sharpen(5);this.filter.contrast(50);this.filter.exposure(10);return this.filter.channels({blue:5});});return this.brightness(10);});Caman.Plugin.register("crop",function(width,height,x,y){var canvas,ctx;if(x==null){x=0;}
|
||||||
|
if(y==null){y=0;}
|
||||||
|
if(typeof exports!=="undefined"&&exports!==null){canvas=new Canvas(width,height);}else{canvas=document.createElement('canvas');canvas.width=width;canvas.height=height;}
|
||||||
|
ctx=canvas.getContext('2d');ctx.drawImage(this.canvas,x,y,width,height,0,0,width,height);return this.replaceCanvas(canvas);});Caman.Plugin.register("resize",function(newDims){var canvas,ctx;if(newDims==null){newDims=null;}
|
||||||
|
if(newDims===null||(!(newDims.width!=null)&&!(newDims.height!=null))){Log.error("Invalid or missing dimensions given for resize");return;}
|
||||||
|
if(!(newDims.width!=null)){newDims.width=this.canvas.width*newDims.height/this.canvas.height;}else if(!(newDims.height!=null)){newDims.height=this.canvas.height*newDims.width/this.canvas.width;}
|
||||||
|
if(typeof exports!=="undefined"&&exports!==null){canvas=new Canvas(newDims.width,newDims.height);}else{canvas=document.createElement('canvas');canvas.width=newDims.width;canvas.height=newDims.height;}
|
||||||
|
ctx=canvas.getContext('2d');ctx.drawImage(this.canvas,0,0,this.canvas.width,this.canvas.height,0,0,newDims.width,newDims.height);return this.replaceCanvas(canvas);});Caman.Filter.register("crop",function(width,height,x,y){if(x==null){x=0;}
|
||||||
|
if(y==null){y=0;}
|
||||||
|
return this.processPlugin("crop",Array.prototype.slice.call(arguments,0));});Caman.Filter.register("resize",function(width,height){return this.processPlugin("resize",Array.prototype.slice.call(arguments,0));});(function(){var BlurStack,mul_table,shg_table;mul_table=[512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,289,287,285,282,280,278,275,273,271,269,267,265,263,261,259];shg_table=[9,11,12,13,13,14,14,15,15,15,15,16,16,16,16,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24];BlurStack=function(){this.r=0;this.g=0;this.b=0;this.a=0;return this.next=null;};Caman.Plugin.register("stackBlur",function(radius){var b_in_sum,b_out_sum,b_sum,div,g_in_sum,g_out_sum,g_sum,height,heightMinus1,i,mul_sum,p,pb,pg,pixels,pr,r_in_sum,r_out_sum,r_sum,radiusPlus1,rbs,shg_sum,stack,stackEnd,stackIn,stackOut,stackStart,sumFactor,w4,width,widthMinus1,x,y,yi,yp,yw,_i,_j,_k,_l,_m,_n,_o,_p,_q;if(isNaN(radius)||radius<1){return;}
|
||||||
|
radius|=0;pixels=this.pixelData;width=this.dimensions.width;height=this.dimensions.height;div=radius+radius+1;w4=width<<2;widthMinus1=width-1;heightMinus1=height-1;radiusPlus1=radius+1;sumFactor=radiusPlus1*(radiusPlus1+1)/2;stackStart=new BlurStack();stack=stackStart;for(i=_i=1;1<=div?_i<div:_i>div;i=1<=div?++_i:--_i){stack=stack.next=new BlurStack();if(i===radiusPlus1){stackEnd=stack;}}
|
||||||
|
stack.next=stackStart;stackIn=null;stackOut=null;yw=yi=0;mul_sum=mul_table[radius];shg_sum=shg_table[radius];for(y=_j=0;0<=height?_j<height:_j>height;y=0<=height?++_j:--_j){r_in_sum=g_in_sum=b_in_sum=r_sum=g_sum=b_sum=0;r_out_sum=radiusPlus1*(pr=pixels[yi]);g_out_sum=radiusPlus1*(pg=pixels[yi+1]);b_out_sum=radiusPlus1*(pb=pixels[yi+2]);r_sum+=sumFactor*pr;g_sum+=sumFactor*pg;b_sum+=sumFactor*pb;stack=stackStart;for(i=_k=0;0<=radiusPlus1?_k<radiusPlus1:_k>radiusPlus1;i=0<=radiusPlus1?++_k:--_k){stack.r=pr;stack.g=pg;stack.b=pb;stack=stack.next;}
|
||||||
|
for(i=_l=1;1<=radiusPlus1?_l<radiusPlus1:_l>radiusPlus1;i=1<=radiusPlus1?++_l:--_l){p=yi+((widthMinus1<i?widthMinus1:i)<<2);r_sum+=(stack.r=(pr=pixels[p]))*(rbs=radiusPlus1-i);g_sum+=(stack.g=(pg=pixels[p+1]))*rbs;b_sum+=(stack.b=(pb=pixels[p+2]))*rbs;r_in_sum+=pr;g_in_sum+=pg;b_in_sum+=pb;stack=stack.next;}
|
||||||
|
stackIn=stackStart;stackOut=stackEnd;for(x=_m=0;0<=width?_m<width:_m>width;x=0<=width?++_m:--_m){pixels[yi]=(r_sum*mul_sum)>>shg_sum;pixels[yi+1]=(g_sum*mul_sum)>>shg_sum;pixels[yi+2]=(b_sum*mul_sum)>>shg_sum;r_sum-=r_out_sum;g_sum-=g_out_sum;b_sum-=b_out_sum;r_out_sum-=stackIn.r;g_out_sum-=stackIn.g;b_out_sum-=stackIn.b;p=(yw+((p=x+radius+1)<widthMinus1?p:widthMinus1))<<2;r_in_sum+=(stackIn.r=pixels[p]);g_in_sum+=(stackIn.g=pixels[p+1]);b_in_sum+=(stackIn.b=pixels[p+2]);r_sum+=r_in_sum;g_sum+=g_in_sum;b_sum+=b_in_sum;stackIn=stackIn.next;r_out_sum+=(pr=stackOut.r);g_out_sum+=(pg=stackOut.g);b_out_sum+=(pb=stackOut.b);r_in_sum-=pr;g_in_sum-=pg;b_in_sum-=pb;stackOut=stackOut.next;yi+=4;}
|
||||||
|
yw+=width;}
|
||||||
|
for(x=_n=0;0<=width?_n<width:_n>width;x=0<=width?++_n:--_n){g_in_sum=b_in_sum=r_in_sum=g_sum=b_sum=r_sum=0;yi=x<<2;r_out_sum=radiusPlus1*(pr=pixels[yi]);g_out_sum=radiusPlus1*(pg=pixels[yi+1]);b_out_sum=radiusPlus1*(pb=pixels[yi+2]);r_sum+=sumFactor*pr;g_sum+=sumFactor*pg;b_sum+=sumFactor*pb;stack=stackStart;for(i=_o=0;0<=radiusPlus1?_o<radiusPlus1:_o>radiusPlus1;i=0<=radiusPlus1?++_o:--_o){stack.r=pr;stack.g=pg;stack.b=pb;stack=stack.next;}
|
||||||
|
yp=width;for(i=_p=1;1<=radius?_p<=radius:_p>=radius;i=1<=radius?++_p:--_p){yi=(yp+x)<<2;r_sum+=(stack.r=(pr=pixels[yi]))*(rbs=radiusPlus1-i);g_sum+=(stack.g=(pg=pixels[yi+1]))*rbs;b_sum+=(stack.b=(pb=pixels[yi+2]))*rbs;r_in_sum+=pr;g_in_sum+=pg;b_in_sum+=pb;stack=stack.next;if(i<heightMinus1){yp+=width;}}
|
||||||
|
yi=x;stackIn=stackStart;stackOut=stackEnd;for(y=_q=0;0<=height?_q<height:_q>height;y=0<=height?++_q:--_q){p=yi<<2;pixels[p]=(r_sum*mul_sum)>>shg_sum;pixels[p+1]=(g_sum*mul_sum)>>shg_sum;pixels[p+2]=(b_sum*mul_sum)>>shg_sum;r_sum-=r_out_sum;g_sum-=g_out_sum;b_sum-=b_out_sum;r_out_sum-=stackIn.r;g_out_sum-=stackIn.g;b_out_sum-=stackIn.b;p=(x+(((p=y+radiusPlus1)<heightMinus1?p:heightMinus1)*width))<<2;r_sum+=(r_in_sum+=(stackIn.r=pixels[p]));g_sum+=(g_in_sum+=(stackIn.g=pixels[p+1]));b_sum+=(b_in_sum+=(stackIn.b=pixels[p+2]));stackIn=stackIn.next;r_out_sum+=(pr=stackOut.r);g_out_sum+=(pg=stackOut.g);b_out_sum+=(pb=stackOut.b);r_in_sum-=pr;g_in_sum-=pg;b_in_sum-=pb;stackOut=stackOut.next;yi+=width;}}
|
||||||
|
return this;});return Caman.Filter.register("stackBlur",function(radius){return this.processPlugin("stackBlur",[radius]);});})();Caman.Filter.register("threshold",function(adjust){return this.process("threshold",function(rgba){var luminance;luminance=(0.2126*rgba.r)+(0.7152*rgba.g)+(0.0722*rgba.b);if(luminance<adjust){rgba.r=0;rgba.g=0;rgba.b=0;}else{rgba.r=255;rgba.g=255;rgba.b=255;}
|
||||||
|
return rgba;});});}).call(this);
|
|
@ -0,0 +1,110 @@
|
||||||
|
let dictionaries = []
|
||||||
|
const imagePromises = [];
|
||||||
|
let image_count = 0
|
||||||
|
|
||||||
|
Caman.Store.put = function() {};
|
||||||
|
|
||||||
|
function load_image(image) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
viewer.addTiledImage({
|
||||||
|
tileSource: image.tileSource,
|
||||||
|
x: image.x,
|
||||||
|
y: image.y,
|
||||||
|
success: () => {
|
||||||
|
resolve();
|
||||||
|
},
|
||||||
|
error: (err) => {
|
||||||
|
reject(err);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch('image/image.txt')
|
||||||
|
.then(response => {
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Network response was not ok');
|
||||||
|
}
|
||||||
|
return response.text();
|
||||||
|
})
|
||||||
|
.then(text => {
|
||||||
|
const lines = text.split('\n');
|
||||||
|
lines.forEach(line => {
|
||||||
|
if (!line.startsWith('#')) {
|
||||||
|
const parts = line.split(' ');
|
||||||
|
|
||||||
|
const dictionary = {
|
||||||
|
imageCount: image_count,
|
||||||
|
tileSource: 'image/dzi/' + parts[0] + '/' + parts[0] + '.dzi',
|
||||||
|
x: parseInt(parts[1]),
|
||||||
|
y: parseInt(parts[2]),
|
||||||
|
red: parseInt(parts[3]),
|
||||||
|
blue: parseInt(parts[4]),
|
||||||
|
green: parseInt(parts[5])
|
||||||
|
};
|
||||||
|
|
||||||
|
const promise = load_image(dictionary);
|
||||||
|
|
||||||
|
dictionaries.push(dictionary);
|
||||||
|
imagePromises.push(promise);
|
||||||
|
image_count++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return Promise.all(imagePromises);
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
let all_filters = []
|
||||||
|
let all_nav_filters = []
|
||||||
|
dictionaries.forEach(image => {
|
||||||
|
let filter = {
|
||||||
|
items: viewer.world.getItemAt(image.imageCount),
|
||||||
|
processors: [
|
||||||
|
function (context, callback) {
|
||||||
|
Caman(context.canvas, function () {
|
||||||
|
this.colorize(image.red, image.green, image.blue, 50);
|
||||||
|
this.render(callback);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// },
|
||||||
|
// function (context, callback) {
|
||||||
|
// Caman(context.canvas, function () {
|
||||||
|
// this.contrast(10);
|
||||||
|
// this.render(callback);
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
let nav_filter = {
|
||||||
|
items: viewer.navigator.world.getItemAt(image.imageCount),
|
||||||
|
processors: [
|
||||||
|
function (context, callback) {
|
||||||
|
Caman(context.canvas, function () {
|
||||||
|
this.colorize(image.red, image.green, image.blue, 50);
|
||||||
|
this.render(callback);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
all_filters.push(filter)
|
||||||
|
all_nav_filters.push(nav_filter)
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
viewer.navigator.setFilterOptions = viewer.setFilterOptions;
|
||||||
|
|
||||||
|
viewer.setFilterOptions({
|
||||||
|
filters: all_filters
|
||||||
|
});
|
||||||
|
|
||||||
|
viewer.navigator.setFilterOptions({
|
||||||
|
filters: all_nav_filters
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Error:', error);
|
||||||
|
});
|
|
@ -0,0 +1,11 @@
|
||||||
|
const clientCountElement = document.getElementById('clientCount');
|
||||||
|
const socket = new WebSocket('wss://sketchersunitedcollab.com/funsocket');
|
||||||
|
|
||||||
|
socket.addEventListener('message', (event) => {
|
||||||
|
const clientCount = parseInt(event.data);
|
||||||
|
let append = " people"
|
||||||
|
if (clientCount === 1) {
|
||||||
|
append = " person"
|
||||||
|
}
|
||||||
|
clientCountElement.textContent = clientCount.toString() + append;
|
||||||
|
});
|
|
@ -0,0 +1,31 @@
|
||||||
|
const modalOverlay = document.getElementById("modal-overlay");
|
||||||
|
const modal = document.getElementById("modal");
|
||||||
|
const openModalBtn = document.getElementById("open-button");
|
||||||
|
const closeModalBtn = document.getElementById("close-button");
|
||||||
|
|
||||||
|
openModalBtn.addEventListener("click", function () {
|
||||||
|
modal.style.display = "block";
|
||||||
|
modalOverlay.style.display = "block";
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
modal.style.opacity = "100";
|
||||||
|
}, 10);
|
||||||
|
});
|
||||||
|
|
||||||
|
closeModalBtn.addEventListener("click", function () {
|
||||||
|
modal.style.display = "none";
|
||||||
|
modalOverlay.style.display = "none";
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
modal.style.opacity = "100";
|
||||||
|
}, 300);
|
||||||
|
});
|
||||||
|
|
||||||
|
modalOverlay.addEventListener("click", function () {
|
||||||
|
modal.style.display = "none";
|
||||||
|
modalOverlay.style.display = "none";
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
modal.style.opacity = "0";
|
||||||
|
}, 300);
|
||||||
|
});
|
|
@ -0,0 +1,28 @@
|
||||||
|
Copyright (C) 2009 CodePlex Foundation
|
||||||
|
Copyright (C) 2010-2022 OpenSeadragon contributors
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
- Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
- Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
- Neither the name of CodePlex Foundation nor the names of its contributors
|
||||||
|
may be used to endorse or promote products derived from this software
|
||||||
|
without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,656 @@
|
||||||
|
OPENSEADRAGON CHANGELOG
|
||||||
|
=======================
|
||||||
|
|
||||||
|
4.1.0:
|
||||||
|
|
||||||
|
* NEW BEHAVIOR: When `navigatorRotate` is false, while the navigator image doesn't rotate, the red outline now does (#2356 @lcl45)
|
||||||
|
* The viewer no longer emits `canvas-key` events for both keydown and keypress events; canvas-key is now just for keydown, and the new `canvas-key-press` is for keypress (#2270 @hrghauri)
|
||||||
|
* You can now specify a priority when calling addHandler, to control when your event handler gets called relative to others (#2273 @Aiosa)
|
||||||
|
* Added tileRetryMax and tileRetryDelay options, so the viewer can retry loading failed tiles (#2238 @Ughuuu @paaddyy, #2334 @Ughuuu @Titan21)
|
||||||
|
* All of the viewers keyboard handling is now in response to keydown events (it used to be split between keydown and keypress) (#2291 @MohitBansal321)
|
||||||
|
* Added `canvas-focus` and `canvas-blur` events to Viewer (#2301 @MohitBansal321)
|
||||||
|
* You can now more easily add custom buttons to the viewer (#2306 @MohitBansal321)
|
||||||
|
* The fitBounds function now takes zoom constraints into account (#2293 @pearcetm)
|
||||||
|
* The viewer now has an `after-resize` event what happens after the viewport bounds have been updated, to complement the `resize` event which happens before (#2317 @pearcetm)
|
||||||
|
* IIIFTileSource now uses resolution level dimensions provided in the info.json "sizes" field for more accurate tile requests (#2337 @ruven)
|
||||||
|
* Added setAjaxHeaders method to Viewer and TiledImage (#2346 @uschmidt83)
|
||||||
|
* Improved documentation (#2297 @KevinBritten)
|
||||||
|
* Fixed: The `tile-loaded` event's completionCallback could be called more than once in some circumstances (#2282 @Aiosa, @pearcetm)
|
||||||
|
* Fixed: Navigator display rectangle was off if the page had `box-sizing: border-box` (#2276 @ambujsahu81)
|
||||||
|
* Fixed: Code that required identifying functions would fail for async functions (#2273 @Aiosa)
|
||||||
|
* Fixed: Reference strip click detection was not accurate for long reference strips (#2280 @damonsson)
|
||||||
|
* Fixed: Translation problems in some circumstances with cropping polygons enabled (#2316 @pearcetm)
|
||||||
|
* Fixed: The navigator area rectangle would grow larger when you zoom in very far (#2318 @donotloveshampo)
|
||||||
|
* Fixed: JSON with embedded XML was being incorrectly identified as XML (#2328 @craigberry)
|
||||||
|
* Fixed: Touch/pinch rotate was not working properly on some platforms (#2324 @rsimon, @pearcetm)
|
||||||
|
* Fixed: Navigator rotation didn't honor `immediately` parameter (#2333 @robertjcolley)
|
||||||
|
* Fixed: The navigator didn't update for its new size in certain circumstances (#2347 @pearcetm)
|
||||||
|
|
||||||
|
4.0.0:
|
||||||
|
|
||||||
|
* NEW BEHAVIOR: Setting the viewport rotation now animates by default (pass true for the new `immediately` parameter to disable) (#2136 @jonasengelmann)
|
||||||
|
* NEW BEHAVIOR: The auto resize now takes both width and height into account when scaling the contents proportionally to the viewer (#2256 @pearcetm)
|
||||||
|
* DEPRECATION: Don't access the viewport's degrees property directly anymore; instead use setRotation and getRotation (#2136 @jonasengelmann)
|
||||||
|
* New gesture: Double-click and drag to zoom (on by default for touch) (#2225 @HamzaTatheer)
|
||||||
|
* You can now provide a pivot point when rotating the viewport (#2233 #2253 @pearcetm)
|
||||||
|
* Improved the constraints that keep the image in the viewer, specifically when zoomed out a lot (#2160 @joedf, #2246 @pearcetm)
|
||||||
|
* You can now provide an element for the navigator (as an alternative to an ID) (#1303 @cameronbaney, #2166 #2175 @joedf)
|
||||||
|
* Now supporting IIIF "id" and "identifier" in addition to "@id" (#2173 @ahankinson)
|
||||||
|
* We now delegate tile fetching and caching to the TileSource, to allow for custom tile formats (#2148 @Aiosa)
|
||||||
|
* Added support for dynamic URLs from tile sources (#2247 @JohnReagan)
|
||||||
|
* The viewer now emits before-destroy and destroy events (#2239 @pearcetm)
|
||||||
|
* Auto resize detection is now more efficient (#2256 @pearcetm)
|
||||||
|
* Improved documentation (#2211 @shyamkumaryadav)
|
||||||
|
* Fixed: Cropping tiled images with polygons was broken (#2183 @altert)
|
||||||
|
* Fixed: Boundary constraints were wrong when the viewport was rotated (#2249 @pearcetm)
|
||||||
|
* Fixed: IIIF tile sizes would be calculated wrong on rare occasions (#2206 @filak)
|
||||||
|
* Fixed: Disabling buttons only changed their appearance, but they were still clickable (#2187 @pearcetm)
|
||||||
|
* Fixed: ImageTileSource produced an error having to do with getTileHashKey (#2190 @Aiosa)
|
||||||
|
* Fixed: On startup you would get an unnecessary "Viewer.buttons is deprecated" warning (#2201 @joedf, #2219 @jssullivan, #2212 @joedf)
|
||||||
|
|
||||||
|
3.1.0:
|
||||||
|
|
||||||
|
* Added subPixelRoundingForTransparency Viewer option to address seams that can appear in semi-transparent images (#2075 @TanukiSharp)
|
||||||
|
* Added Viewer.isAnimating() (#2075 @TanukiSharp)
|
||||||
|
* Added isFullScreen method to Viewer (#2067 @JachiOnuoha)
|
||||||
|
* Added option to include POST data when loading files via Ajax (#2072 @Aiosa)
|
||||||
|
* Exposed TiledImage's private functions for better maintainability (#2134 @Aiosa)
|
||||||
|
* Tile cache keys are now generated by the tile source, so it's easier to override them as needed (#2138 @Aiosa)
|
||||||
|
* Pinch to zoom now zooms around the center of the pinch, rather than the center of the viewer (#2158 @cavenel)
|
||||||
|
* Added fallback and deprecation warning for Viewer.buttons (which got changed to buttonGroup in 3.0.0) (#2153 @devbyjonah)
|
||||||
|
* Fixed an issue where turning off panVertical or panHorizontal would not affect the panning keyboard combos (#2069 @JachiOnuoha)
|
||||||
|
* Cleaned up console.logs so that errors and warnings use console.error and console.warn as appropriate (#2073 @Abhishek-90)
|
||||||
|
* Improved documentation (#2067 @JachiOnuoha, #2112 @shyamkumaryadav, #2152 @joedf, #2155 @samwilson)
|
||||||
|
* Fixed: Setting useCanvas to false would break the viewer (#2116 @rvv-bouvet)
|
||||||
|
* Allow silencing multi-image warnings on viewport coordinate conversion functions (#2120 @claycoleman)
|
||||||
|
* Fixed: Swiping fast multiple times made contact points in MouseTracker out of sync for touch events (#2121 @ronnymikalsen)
|
||||||
|
* Made MouseTracker more robust in certain scenarios (#2134, #2147 @Aiosa)
|
||||||
|
* Fixed an issue where full page mode wouldn't grow properly if you resized the window (#2100 @TanukiSharp)
|
||||||
|
* Now if you pass an error handler into makeAjaxRequest, it doesn't report errors into the console (#2142 @Aiosa)
|
||||||
|
* Fixed error caused by attaching MouseTracker to the page's document element (#2145 @tdiprima)
|
||||||
|
* Fixed an issue that would sometimes cause problems with freeing up ImageTileSource memory (#2162 @pearcetm)
|
||||||
|
|
||||||
|
3.0.0:
|
||||||
|
|
||||||
|
* BREAKING CHANGE: Dropped support for older browsers (IE < 11) (#1872 #1949 #1951 @msalsbery, #1950 @rmontroy)
|
||||||
|
* BREAKING CHANGE: Removed deprecated OpenSeadragon.getEvent function (#1949 @msalsbery)
|
||||||
|
* DEPRECATION: MouseTracker exitHandler deprecated for name change to leaveHandler for consistency with DOM event names (#1872 @msalsbery)
|
||||||
|
* Now when "simple image" tile sources are removed from the viewer, they free the memory used by the pyramid they create (#1789 @TakumaKira)
|
||||||
|
* Improvements to docs (#1814 @kenanchristian, #1872 @msalsbery, #1996 @tdiprima)
|
||||||
|
* Better cleanup on destruction, to avoid memory leaks (#1832 @JoFrMueller)
|
||||||
|
* Better handle destruction when navigator in custom location (#1884 @woodchuck)
|
||||||
|
* Miscellaneous code cleanup (#1840 @msalsbery)
|
||||||
|
* You can now specify tileSize for the Zoomify Tile Source (#1868 @abrlam)
|
||||||
|
* Better use of IIIF "max" and "full" URL parameters (#1871 @MImranAsghar)
|
||||||
|
* You can now specify the file format of the tiles in the Zoomify tile source (#1889 @abrlam)
|
||||||
|
* Improved browser sniffing - detect EDGE and CHROMEEDGE browsers (#1872 @msalsbery)
|
||||||
|
* Improved DOM event model feature detection (#1872 @msalsbery)
|
||||||
|
* Added support for options parameter on addEvent()/removeEvent (to support passive option) (#1872 @msalsbery)
|
||||||
|
* Added OpenSeadragon.eventIsCanceled() function for defaultPrevented detection on DOM events (#1872 @msalsbery)
|
||||||
|
* MouseTracker: better PointerEvent model detection - removed use of deprecated window.navigator.pointerEnabled (#1872 @msalsbery)
|
||||||
|
* MouseTracker: added overHandler/outHandler options for handling corresponding pointerover/pointerout events (#1872 @msalsbery)
|
||||||
|
* MouseTracker: changed enterHandler/leaveHandler to use DOM pointerenter/pointerleave events instead of simulating using pointerover/pointerout (#1872 @msalsbery)
|
||||||
|
* All internal uses of MouseTracker use pointerenter/pointerleave events instead of pointerover/pointerout events for more consistent pointer tracking (#1872 @msalsbery)
|
||||||
|
* Fixed bug in Button class where two MouseTracker event handlers used an invalid "this" causing issues in some browsers (#1872 @msalsbery)
|
||||||
|
* Added pointerType property to Viewer container-enter, container-exit, canvas-drag, canvas-drag-end, canvas-pinch events (#1872 @msalsbery)
|
||||||
|
* MouseTracker: Fire dragEndHandler event even if release point same as initial contact point (#1872 @msalsbery)
|
||||||
|
* MouseTracker: Pointer capture implemented with capture APIs where available. Only fallback to emulated capture on extremely old browsers (#1872 @msalsbery)
|
||||||
|
* MouseTracker: Added preProcessEventHandler option to allow MouseTracker instances to control bubbling and default behavior of events on their associated element (#1872 @msalsbery)
|
||||||
|
* MouseTracker: Improved handling of canceled events (#1872 @msalsbery)
|
||||||
|
* MouseTracker: Improved releasing of tracked pointers on destroy()/stopTracking() (#1872 @msalsbery)
|
||||||
|
* Updated Viewer, Button, Drawer, Navigator, ReferenceStrip DOM for proper DOM event handling (#1872 @msalsbery)
|
||||||
|
* Added OpenSeadragon.setElementPointerEventsNone() for setting pointer-events:'none' on DOM elements (#1872 @msalsbery)
|
||||||
|
* MouseTracker: added contextMenuHandler option for handling contextmenu events (#1872 @msalsbery)
|
||||||
|
* Viewer: added a canvas-contextmenu event (#1872 @msalsbery)
|
||||||
|
* Fixed simulated drag events in navigator tests (#1949 @msalsbery)
|
||||||
|
* Added preventDefault option to MouseTracker.contextMenuHandler and Viewer 'canvas-contextmenu' event args (#1951 @msalsbery)
|
||||||
|
* MouseTracker: Added preProcessEventHandler for keydown, keyup, keypress, focus, blur Events (#1951 @msalsbery)
|
||||||
|
* Fixed preventDefaultAction functionality in viewer events (#1953 @msalsbery)
|
||||||
|
* Added setImageFormatsSupported function (#1954 @pandaxtc)
|
||||||
|
* Added dragToPan to the GestureSettings class, implemented in Viewer (#1956 @msalsbery)
|
||||||
|
* Added preventDefault option to MouseTracker handlers: scrollHandler, keyDownHandler, keyUpHandler, keyHandler (#1957 @msalsbery)
|
||||||
|
* Fixed test "Events: Viewer: preventDefaultAction in dblClickHandler". Fixes #1372 (#1960 @msalsbery)
|
||||||
|
* ReferenceStrip: Fixed issue where its element was being removed from its parent element twice on destroy, causing an exception (#1958 @msalsbery)
|
||||||
|
* ReferenceStrip: Made its element focusable for keyboard navigation (#1958 @msalsbery)
|
||||||
|
* You can now flip individual images (not just the whole viewport) (#1903 @ali1234)
|
||||||
|
* Accessibility: we now take the browser's zoom into account when choosing what detail level to draw (#1937 @ronnymikalsen)
|
||||||
|
* Fixed a bug causing overlays to disappear in Sequence Mode (#1865 @gunmiosb)
|
||||||
|
* Fixed a bug where the ajaxHeaders provided per-image were not being used for image requests (#1968 @maxshuty)
|
||||||
|
* MouseTracker: Added workaround for WebKit Pointer Event Implicit Capture Bug (#1972 @msalsbery)
|
||||||
|
* Removed test for move-leave (fly-over, no enter event)...not a valid, handleable event state, no longer supported (#1972 @msalsbery)
|
||||||
|
* Added OpenSeadragon.setElementPointerEvents() for setting pointer-events to other values besides 'none' on DOM elements (#1972 @msalsbery)
|
||||||
|
* Now ensuring the page body is display:block when in fullscreen (#1995 @thewilkybarkid)
|
||||||
|
* Added a static method in OpenSeadragon to get an existing viewer (#2000 @HerCerM)
|
||||||
|
* Now ensuring that the new item is already in the navigator when the "add-item" event fires (#2005 @RammasEchor)
|
||||||
|
* Added keys to change image in sequence mode (j: previous, k: next) (#2007 @RammasEchor)
|
||||||
|
* Fixed a bug where the navigator wouldn't pick up opacity/composite changes made while it is loading (#2018 @crydell)
|
||||||
|
* Explicitly set passive:false for wheel event handlers to suppress console warnings. Fixes #1669 (#2043 @msalsbery)
|
||||||
|
* Viewer's canvas-click events now include an originalTarget property so you can know which element received the click (#2037 @iangilman)
|
||||||
|
* Added method for getting the size of an image in window coordinates (#2049 @superbland)
|
||||||
|
* Added a setMaxLevel function to TileSource so you can change its maxLevel if needed (#2059, #2066 @kim-sanghoon)
|
||||||
|
|
||||||
|
2.4.2:
|
||||||
|
|
||||||
|
* Add support for IIIF Image API 3.0 beta (#1764)
|
||||||
|
* You can now crop an image with arbitrary polygons (#1772)
|
||||||
|
* Improved support for using the Reference Strip in an OpenSeadragon Viewer inside a Web Component (#1676)
|
||||||
|
* Added setWidth and setHeight methods to Navigator (#1686)
|
||||||
|
* Improvements to docs (#1696, #1698, #1716, #1719)
|
||||||
|
* Now passing Viewer AJAX configs down to ReferenceStrip thumbnails (#1701)
|
||||||
|
* The ReferenceStrip now honors the useCanvas option from the Viewer (#1742)
|
||||||
|
* Fixed: Navigator was still resizing after you explicitly set its width and height with navigatorWidth and navigatorHeight (#1686)
|
||||||
|
* Fixed issues with touches on iOS 13 and iPad (#1754, #1756)
|
||||||
|
* No longer throwing an exception on pages that have malformed URL parameters (#1758)
|
||||||
|
* Fixed an issue with flipping the viewport on high pixel density screens (#1779)
|
||||||
|
* Removed use of deprecated imageSmoothingEnabled prefixes (#1740)
|
||||||
|
|
||||||
|
2.4.1:
|
||||||
|
|
||||||
|
* You can now turn off the default canvas image smoothing, if you want sharp pixels when zoomed in past 100% (#1507, #1593, #1595, #1647, #1648)
|
||||||
|
* Fixed problem with navigator highlight rectangle when returning from full screen with a custom navigator location (#1515)
|
||||||
|
* Added option to set rotation increment for nav buttons and keyboard (#1524)
|
||||||
|
* Fixed issue with flipping and opacity with multi-image (#1549)
|
||||||
|
* Removed vestigial button group label element that was causing issues for accessibility tools (#1560)
|
||||||
|
* Fixed a bug causing Viewer.areControlsEnabled to throw an exception (#1562)
|
||||||
|
* Added tileFormat option to IIIFTileSource so you can specify the tile format (#1625)
|
||||||
|
* Now using canonical URIs more consistently in IIIF to make caching and processing time easier on the server side (#1625)
|
||||||
|
* Added support for IIIF's new preferredFormats property (#1656)
|
||||||
|
|
||||||
|
2.4.0:
|
||||||
|
|
||||||
|
* BREAKING CHANGE: Viewer's canvas-double-click event is now fired before it initiates the zoom (#1288)
|
||||||
|
* You can now flip the viewport to get a mirror image of the original (#1441)
|
||||||
|
* You can now prevent canvas-double-click events from zooming on a per-event basis (#1288)
|
||||||
|
* Fixed: Opacity 0 images were causing unnecessary redraws (#1319)
|
||||||
|
* The "page" event is now fired after the page index has been updated (#1330)
|
||||||
|
* Added option pixelsPerArrowPress that sets the speed of arrow keys (#1364)
|
||||||
|
* Improved IIIF options.maxLevel calculation (#1401)
|
||||||
|
* Added canvas-key events, along with the ability to cancel key actions (#1414)
|
||||||
|
* Added optional zoom in the middle of the image instead of pointer position (#1423)
|
||||||
|
* Now supporting square edge tiles that are padded rather than cropped (#1426)
|
||||||
|
* Fixed an issue causing the simple image tileSource to sometimes show duplicate copies (#1370)
|
||||||
|
* Fixed an issue causing seams to appear in semi-transparent PNG tiled images (#1470)
|
||||||
|
* Added visual customization options for the navigator (#1480)
|
||||||
|
* You can now prevent canvas-drag events on the navigator (#1484)
|
||||||
|
* You can now prevent canvas-click events on the navigator (#1416)
|
||||||
|
* The navigator can now be restricted to just horizontal or just vertical panning (#1416)
|
||||||
|
* Fixed DziTileSource so it doesn't load levels above maxLevel or below minLevel, if set (#1492)
|
||||||
|
|
||||||
|
2.3.1:
|
||||||
|
|
||||||
|
* Debug mode now uses different colors for different tiled images (customizable via debugGridColor) (#1271)
|
||||||
|
* Fixed a problem with preventDefaultAction for the canvas-drag event (#1278)
|
||||||
|
* Fixed an issue causing double images with certain aspect ratios (#1280)
|
||||||
|
* Fixed: placeholderFillStyle had no effect (#1286)
|
||||||
|
* Fixed seams that appear in wrap mode on Safari and Firefox (#1305)
|
||||||
|
|
||||||
|
2.3.0:
|
||||||
|
|
||||||
|
* BREAKING CHANGE: Tile.distance has been removed (#1027)
|
||||||
|
* BREAKING CHANGE: Viewer's canvas-click event is now fired before it initiates the zoom (#1148)
|
||||||
|
* BREAKING CHANGE: Viewer's canvas-drag event is now fired before it pans (#1149)
|
||||||
|
* Added Zoomify tile source (#863)
|
||||||
|
* You can now set the rotation of individual tiled images (#1006)
|
||||||
|
* Added getFullyLoaded method and "fully-loaded-change" event to TiledImage to know when tiles are fully loaded (#837, #1073)
|
||||||
|
* You can now preload images without drawing them to the screen (#1071)
|
||||||
|
* Added support for commonjs (#984)
|
||||||
|
* Added an option to addTiledImage to change the crossOriginPolicy (#981)
|
||||||
|
* You can now load tiles via AJAX and custom AJAX request headers (#1055)
|
||||||
|
* Added ability to provide thumbnail URLs for reference strip (#1241)
|
||||||
|
* Improved panning constraints for constrainDuringPan (#1133 and #1245)
|
||||||
|
* You can now prevent canvas-click events from zooming on a per-event basis (#1148)
|
||||||
|
* You can now prevent canvas-drag events from panning on a per-event basis (#1149)
|
||||||
|
* The navigationControlAnchor option now works for custom toolbar as well (#1004)
|
||||||
|
* LegacyTileSource now allows any image URLs regardless of type (#1056)
|
||||||
|
* Enabled configuration of ImageLoader timeout (#1192)
|
||||||
|
* Viewer.open() now supports an initialPage argument for sequenceMode (#1196)
|
||||||
|
* New events for opacity and compositeOperation changes (#1203)
|
||||||
|
* Added support for setting debug mode after the Viewer object has been constructed (#1224)
|
||||||
|
* Added functions for dynamically adding and removing the reference strip in sequence mode (#1213)
|
||||||
|
* Better calculation for TileCache release cutoff (#1214)
|
||||||
|
* The navigator now picks up opacity and compositeOperation changes (#1203)
|
||||||
|
* Improved calculation for determining which level to load first (#1198)
|
||||||
|
* Added fix for supporting weird filenames that look like JSONs (#1189)
|
||||||
|
* Improved DziTileSource guessing of tilesUrl (#1074)
|
||||||
|
* The Viewer's tileSources option is now smarter about detecting JSON vs XML vs URL (#999)
|
||||||
|
* Better compression for our UI images (#1134)
|
||||||
|
* Optimization: Use the squared distance when comparing tiles (#1027)
|
||||||
|
* Now clamping pixel ratio density to a minimum of 1, fixing display issues on low density devices (#1200)
|
||||||
|
* More forgiving check for DZI schema (#1249)
|
||||||
|
* ImageTileSource now works in IE8 (#1041)
|
||||||
|
* Fixed CORS bug in IE 10 (#967)
|
||||||
|
* Fixed issue with tiles not appearing with wrapHorizontal/wrapVertical if you pan too far away from the origin (#987, #1066)
|
||||||
|
* Fixed: Initial tile load wasn't happening in parallel (#1014)
|
||||||
|
* Fixed problem with "sparse image" DZI files (#995)
|
||||||
|
* Fix IndexSizeError on IE and Edge that occurred under certain circumstances (e.g. multi-image with transparency) (#1035)
|
||||||
|
* Fixed error in IE8 when zooming in (due to edge smoothing) (#1064)
|
||||||
|
* Fixed issue with OpenSeadragon.version in the minified JavaScript (#1099)
|
||||||
|
* Fixed smoothTileEdgesMinZoom performance degradation on single-tile images (#1101)
|
||||||
|
* Fixed issue with tiles not appearing after rotation (#1102)
|
||||||
|
* Fixed: The navigator wasn't respecting the constrainDuringPan setting (#1104)
|
||||||
|
* Fixed an issue causing overlays to be mis-positioned in some circumstances (#1119)
|
||||||
|
* Fixed: ImageTileSource would sometimes produce a double image (#1123)
|
||||||
|
* Fixed: console.debug caused exceptions on IE10 (#1129)
|
||||||
|
* Fixed: the reference strip would leak memory when opening new sets of images (#1175)
|
||||||
|
* Fixed: zoomTo/zoomBy ignore refPoint if immediately is true (#1184)
|
||||||
|
* Fixed: IIPImageServer didn't work with the latest OSD release (#1199)
|
||||||
|
* Fixed: setItemIndex method not working with navigator inside "open" event (#1201)
|
||||||
|
* Fixed: The reference strip didn't show the initial page if it wasn't the first page (#1208)
|
||||||
|
* Fixed: Sometimes the image would stick to the mouse when right-clicking and left-clicking simultaneously (#1223)
|
||||||
|
* Fixed issue with transparent images sometimes disappearing on Safari (#1222)
|
||||||
|
* Fixed: One image failing to load could cause the others to never load (#1229)
|
||||||
|
* Fixed: Mouse up outside map will cause "canvas-drag" event to stick (#1133)
|
||||||
|
* Fixed more issues with tracking multiple pointers (#1244)
|
||||||
|
|
||||||
|
2.2.1:
|
||||||
|
|
||||||
|
* Fixed problems with zoom/pan constraints with certain extreme settings (#965)
|
||||||
|
* Fixed an issue causing the browser to crash on iOS (#966)
|
||||||
|
|
||||||
|
2.2.0:
|
||||||
|
|
||||||
|
* BREAKING CHANGE: Viewport.homeBounds, Viewport.contentSize, Viewport.contentAspectX and
|
||||||
|
Viewport.contentAspectY have been removed. (#846)
|
||||||
|
* BREAKING CHANGE: The Overlay.getBounds method now takes the viewport as parameter. (#896)
|
||||||
|
* DEPRECATION: Overlay.scales, Overlay.bounds and Overlay.position have been deprecated. (#896)
|
||||||
|
* Overlay.width !== null should be used to test whether the overlay scales horizontally
|
||||||
|
* Overlay.height !== null should be used to test whether the overlay scales vertically
|
||||||
|
* The Overlay.getBounds method should be used to get the bounds of the overlay in viewport coordinates
|
||||||
|
* Overlay.location replaces Overlay.position
|
||||||
|
* DEPRECATION: Viewport.setHomeBounds has been deprecated (#846)
|
||||||
|
* DEPRECATION: the Viewport constructor is now ignoring the contentSize option (#846)
|
||||||
|
* Tile edge smoothing at high zoom (#764)
|
||||||
|
* Fixed issue with reference strip popping up virtual keyboard on mobile devices (#779)
|
||||||
|
* Now supporting rotation in the Rect class (#782)
|
||||||
|
* Drag outside of iframe now works better, as long as both pages are on the same domain (#790)
|
||||||
|
* Coordinate conversion now takes rotation into account (#796)
|
||||||
|
* Support tile-less IIIF as per LegacyTileSource (#816)
|
||||||
|
* You can now give an empty string to the tabIndex option (#805)
|
||||||
|
* Fixed issue with rotation and clicking in the navigator (#807)
|
||||||
|
* Broadened the check for mime type in LegacyTileSource URLs to allow query strings (#819)
|
||||||
|
* Added globalCompositeOperation option for tiledImage, to allow for different transfer modes (#814)
|
||||||
|
* Added Viewer.addSimpleImage method for easily adding non-tiled images (#827)
|
||||||
|
* DziTileSource now works properly with DZI files that have no extension (#835)
|
||||||
|
* Fixed content clipping with rotation (#463, #567 and #833)
|
||||||
|
* Fixed navigator not being rotated when viewport rotation is set in constructor (#840)
|
||||||
|
* Fixed: Viewer.setMouseNavEnabled wasn't affecting all of the viewer's trackers (#845)
|
||||||
|
* Fixed: with scrollToZoom disabled, the viewer caused page scrolling to slow down (#858)
|
||||||
|
* Added Viewer.getOverlayById and Overlay.getBounds functions (#853)
|
||||||
|
* Tiled images with 0 opacity no longer load their tiles or do drawing calculations (#859)
|
||||||
|
* Fixed issue with edge smoothing with PNG tiles at high zoom (#860)
|
||||||
|
* Fixed: Images with transparency were clearing images layered below them (#861)
|
||||||
|
* Fixed issue causing HTML pages to jump unwantedly to the reference strip upon loading (#872)
|
||||||
|
* Added addOnceHandler method to EventSource (#887)
|
||||||
|
* Added TiledImage.fitBounds method (#888)
|
||||||
|
* Overlays can now be scaled in a single dimension by providing a point location and either width or height (#896)
|
||||||
|
* Added full rotation support to overlays (#729, #193)
|
||||||
|
* Viewport.goHome() now takes clipping into account (#910)
|
||||||
|
* Improved zoom to point (#923)
|
||||||
|
* Optimized sketch canvas clearing and blending for images with opacity or transfer modes (#927)
|
||||||
|
* Now taking rotation into account in viewport getBounds and fitBounds methods (#934)
|
||||||
|
* Added option to disable navigator auto-fade (#935)
|
||||||
|
* Fixed issue with maintaining viewport position with full screen (#940)
|
||||||
|
* Fixed an issue with simultaneous touch events (#930)
|
||||||
|
* Avoid loading clipped out tiles (#939)
|
||||||
|
* Improved precision for subtle moves with fitBounds (#939)
|
||||||
|
* Fixed an issue in viewer.addTiledImage with replace:true when viewer has navigator (#948)
|
||||||
|
|
||||||
|
2.1.0:
|
||||||
|
|
||||||
|
* BREAKING CHANGE: the tile does not hold a reference to its image anymore. Only the tile cache keep a reference to images.
|
||||||
|
* BREAKING CHANGE: TileSource.tileSize no longer exists; use TileSource.getTileWidth() and TileSource.getTileHeight() instead.
|
||||||
|
* DEPRECATION: let ImageRecord.getRenderedContext create the rendered context instead of using ImageRecord.setRenderedContext
|
||||||
|
* DEPRECATION: TileSource.getTileSize() is deprecated. Use TileSource.getTileWidth() and TileSource.getTileHeight() instead.
|
||||||
|
* Changed resize behaviour to prevent "snapping" to world bounds when constraints allow more space (#711)
|
||||||
|
* Added support for non-square tiles (#673)
|
||||||
|
* TileSource.Options objects can now optionally provide tileWidth/tileHeight instead of tileSize for non-square tile support.
|
||||||
|
* IIIFTileSources will now respect non-square tiles if available.
|
||||||
|
* Added new tile source for simple images: ImageTileSource (#760)
|
||||||
|
* Optimized adding large numbers of items to the world with collectionMode (#735)
|
||||||
|
* Registers as an AMD module where possible (#719)
|
||||||
|
* Added "tile-loaded" event on the viewer allowing to modify a tile before it is marked ready to be drawn (#659)
|
||||||
|
* Added "tile-unloaded" event on the viewer allowing to free up memory one has allocated on a tile (#659)
|
||||||
|
* Added 'tile-load-failed' event (#725)
|
||||||
|
* Added additional coordinates conversion methods to TiledImage (#662)
|
||||||
|
* Added `preserveImageSizeOnResize` option (#666)
|
||||||
|
* Added collectionColumns as a configuration parameter (#680)
|
||||||
|
* Added option in addTiledImage to replace tiledImage at index (#706)
|
||||||
|
* Added autoRefigureSizes flag to World for optimizing mass rearrangements (#715)
|
||||||
|
* You can now change viewport margins after the viewer is created (#721)
|
||||||
|
* Added a patch to help slow down the scroll devices that fire too fast (#754)
|
||||||
|
* Fixed flickering tiles with useCanvas=false when no cache is used (#661)
|
||||||
|
* 'display: none' no longer gets reset on overlays during draw (#668)
|
||||||
|
* Better error reporting for tile load failures (#679)
|
||||||
|
* Added XDomainRequest as fallback method for ajax requests if XMLHttpRequest fails (for IE < 10) (#693)
|
||||||
|
* Now avoiding using eval when JSON.parse is available (#696)
|
||||||
|
* Rotation now works properly on retina display (#708)
|
||||||
|
* Fixed issue with tiledImages loading tiles at every level instead of just the best level (#728)
|
||||||
|
* Fixed placeholderFillStyle flicker (#727)
|
||||||
|
* Fix for Chrome (v45) issue that key is sometimes undefined outside of the for-in loop (#730)
|
||||||
|
* World.removeAll now cancels any in-flight image loads; same for Viewer.open and Viewer.close (#734)
|
||||||
|
* Fixed overlays position (use rounding instead of flooring and ceiling) (#741)
|
||||||
|
* Fixed issue with including overlays in your tileSources array when creating/opening in the viewer (#745)
|
||||||
|
* Fixed issue in iOS devices that would cause all touch events to fail after a Multitasking Gesture was triggered (#744)
|
||||||
|
* Fixed an issue with TiledImage setPosition/setWidth/setHeight not reliably triggering a redraw (#720)
|
||||||
|
* Fixed zooming in with plus key on a Swedish keyboard (#763)
|
||||||
|
|
||||||
|
2.0.0:
|
||||||
|
|
||||||
|
* True multi-image mode (#450)
|
||||||
|
* BREAKING CHANGE: Passing an array for the tileSources option is no longer enough to trigger sequence mode; you have to set the sequenceMode option to true as well
|
||||||
|
* BREAKING CHANGE: Navigator no longer sends an open event when its viewer opens
|
||||||
|
* BREAKING CHANGE: Viewer.drawers and Viewer.drawersContainer no longer exist
|
||||||
|
* BREAKING CHANGE: A Viewer's Drawer and Viewport are now made once per Viewer and reused for every image that Viewer opens (rather than being recreated for every open); this means if you change Viewer options between opens, the behavior is different now.
|
||||||
|
* DEPRECATION: use Viewer.addTiledImage instead of Viewer.addLayer
|
||||||
|
* addTiledImage supports positioning config properties
|
||||||
|
* DEPRECATION: use World.getItemAt instead of Viewer.getLayerAtLevel
|
||||||
|
* DEPRECATION: use World.getIndexOfItem instead of Viewer.getLevelOfLayer
|
||||||
|
* DEPRECATION: use World.getItemCount instead of Viewer.getLayersCount
|
||||||
|
* DEPRECATION: use World.setItemIndex instead of Viewer.setLayerLevel
|
||||||
|
* DEPRECATION: use World.removeItem instead of Viewer.removeLayer
|
||||||
|
* DEPRECATION: use World.needsDraw instead of Drawer.needsUpdate
|
||||||
|
* DEPRECATION: use TileCache.numTilesLoaded instead of Drawer.numTilesLoaded
|
||||||
|
* DEPRECATION: use World.resetItems instead of Drawer.reset
|
||||||
|
* DEPRECATION: use Drawer.clear and World.draw instead of Drawer.update
|
||||||
|
* DEPRECATION: the layersAspectRatioEpsilon option is no longer necessary
|
||||||
|
* DEPRECATION: Viewer's add-layer event is now World's add-item event
|
||||||
|
* DEPRECATION: Viewer's layer-level-changed event is now World's item-index-change event
|
||||||
|
* DEPRECATION: Viewer's remove-layer event is now World's remove-item event
|
||||||
|
* DEPRECATION: Viewer's add-layer-failed event is now add-item-failed
|
||||||
|
* DEPRECATION: TileSourceCollection has been retired in favor of World
|
||||||
|
* DEPRECATION: collectionMode no longer draws outlines or reflections for items
|
||||||
|
* Drawer has been split into three classes:
|
||||||
|
* TiledImage, tile management and positioning for a single tiled image
|
||||||
|
* TileCache, tile caching for all images
|
||||||
|
* Drawer, tile drawing for all images
|
||||||
|
* New class: World, keeps track of multiple images in the scene
|
||||||
|
* Viewer now has world and tileCache properties
|
||||||
|
* Rect and Point now have clone functions
|
||||||
|
* New Viewport method for managing homeBounds as well as constraints: setHomeBounds
|
||||||
|
* Viewport.open supports positioning config properties
|
||||||
|
* For multi-image open, drawing isn't started until all tileSources have been opened
|
||||||
|
* You can specify a clip area for each image (only works on browsers that support the HTML5 canvas) (#594)
|
||||||
|
* Added placeholderFillStyle so image rectangles can be drawn even before their tiles load (#635)
|
||||||
|
* Ability to set opacity on individual TiledImages (#644)
|
||||||
|
* Margins option to push the home region in from the edges of the Viewer (#505)
|
||||||
|
* Rect and Point toString() functions are now consistent: rounding values to nearest hundredth
|
||||||
|
* Overlays appear in the DOM immediately on open or addOverlay (#507)
|
||||||
|
* imageLoaderLimit now works (#544)
|
||||||
|
* Turning off scrollToZoom in gestureSettings now allows scroll events to propagate
|
||||||
|
* You can now set a minZoomLevel that's greater than the home zoom level
|
||||||
|
* Added union() to OpenSeadragon.Rect
|
||||||
|
* Fixed an error in fitBounds if the new and old bounds were extremely close in size
|
||||||
|
* Added ajaxWithCredentials option (#543)
|
||||||
|
* Added viewport-change event for after the viewport changes but before it's drawn
|
||||||
|
* A spring's current value is now updated immediately on reset (#524)
|
||||||
|
* Fixed an error in fitBounds that occurred sometimes with immediately = true
|
||||||
|
* Added support for HDPI (retina) displays (#583)
|
||||||
|
* Corrected IIIF tile source to use canonical syntax (#586)
|
||||||
|
* Fixed x/y typo that caused horizontal reference strip to be rendered only relative to height (#595)
|
||||||
|
* Fixed Firefox 35 not able to open local files (#588)
|
||||||
|
* Fixed an issue with zero size viewers in IE8 (#609)
|
||||||
|
* Fixed: Cross Origin policy not working (#613)
|
||||||
|
* Optimized tile loading by clearing the queue on a re-draw when imageLoaderLimit is set (#616)
|
||||||
|
* Now animating zoom spring exponentially (#631)
|
||||||
|
* Added http://editorconfig.org/ config file (#637)
|
||||||
|
* Keyboard pan speed is now the same regardless of zoom level (#645)
|
||||||
|
|
||||||
|
1.2.1:
|
||||||
|
|
||||||
|
* Added preserveOverlays option (#561)
|
||||||
|
* Fixed: DZI tilesource was broken on IE8/IE9 (#563)
|
||||||
|
* Exposed secondary pointer button (middle, right, etc.) events from MouseTracker and through viewer (#479)
|
||||||
|
* MouseTracker - Improved IE 8 compatibility (#562)
|
||||||
|
* MouseTracker - Improved IE 9+ compatibility (#564)
|
||||||
|
* MouseTracker - Simulated touchenter/touchleave events now bubble to parent element MouseTrackers (#566)
|
||||||
|
* MouseTracker - Improved multitouch support in enter/exit event handlers (#566)
|
||||||
|
* MouseTracker - orphaned tracked touch pointers removed (fix for #539)
|
||||||
|
* MouseTracker - removed touchenter/touchleave event support since the events don't exist on any known platform and have been removed from the W3C specification (#566)
|
||||||
|
* Removed Viewer onContainerPress/onContainerRelease handlers (and the associated 'container-release' event ) that were never fired due to the canvas (child) element capturing the DOM events (#566)
|
||||||
|
* Added 'canvas-enter', 'canvas-exit', and 'canvas-press' events to Viewer (#566)
|
||||||
|
* ButtonGroup - removed obsolete MouseTracker event handlers (#566)
|
||||||
|
* MouseTracker - added keydown and keyup handlers (#568)
|
||||||
|
* Modifier keys ignored in keyboard navigation handlers (#503)
|
||||||
|
* Requesting keyboard focus when viewer is clicked (#537)
|
||||||
|
* Arrow key navigation fixed across platforms (#565)
|
||||||
|
* Removed textarea element from viewer DOM. Viewer.canvas now handles keyboard navigation (#569)
|
||||||
|
* Removed 'position' property from MouseTracker keyDownHandler/keyUpHandler/keyHandler functions (#573)
|
||||||
|
* Fixed pointer event model detection for IE 10 and IE 11 (#571)
|
||||||
|
* Added setMouseNavEnabled() support to Navigator (#572)
|
||||||
|
* MouseTracker now defaults to tracking on (#558)
|
||||||
|
* Removed Viewer focusHandler/onCanvasFocus (#577)
|
||||||
|
* Added tabIndex option to viewer (#577)
|
||||||
|
|
||||||
|
1.2.0:
|
||||||
|
|
||||||
|
* New combined IIIF TileSource for 1.0 through 2.0 (#441)
|
||||||
|
* BREAKING CHANGE: Removed IIIF1_1TileSource (now that IIIFTileSource supports all versions)
|
||||||
|
* Allowed TileSources to have dynamic tileSize via source.getTileSize(level) (#441)
|
||||||
|
* DEPRECATION: Use .getTileSize(level) instead of .tileSize
|
||||||
|
* Fix for IIPServer-style urls when using DZI (#413)
|
||||||
|
* Fix memory leak while destroying the viewer (#421)
|
||||||
|
* Added fitBoundsWithConstraints() to the viewport (#423)
|
||||||
|
* Fixed MouseTracker cross-browser issues with tracking pointers over and out of the tracked element (pull request #448, fix for #152, #404, #420, and #427)
|
||||||
|
* Fixed incorrect flick direction after image is rotated (#452)
|
||||||
|
* Debug mode now works with rotate images (#453)
|
||||||
|
* Now supporting dzi xml with namespaces (#462)
|
||||||
|
* You can now rotate the navigator along with the main viewer (#455)
|
||||||
|
* Viewport.setRotation now allows all rotation angles (#466)
|
||||||
|
* Pinch rotate is now available (defaults to off) (#468)
|
||||||
|
* Added option for home button to fill viewer (#474)
|
||||||
|
* Better handling of mid-update image loaded callbacks (#409)
|
||||||
|
* Tracked pointers are now cleaned up when Viewer.setMouseNavEnabled(false) is called (#518)
|
||||||
|
* Added explicit pointer capture for touch event model touchstart events (#552)
|
||||||
|
|
||||||
|
1.1.1:
|
||||||
|
|
||||||
|
* Fixed issue with dragging the navigator highlight on Webkit browsers (#395)
|
||||||
|
* Improved Viewer Options Support in Gesture Handling (#399)
|
||||||
|
|
||||||
|
1.1.0:
|
||||||
|
|
||||||
|
* BREAKING CHANGE: the openseadragon-canvas element now has two child divs. This means: (#298)
|
||||||
|
* The drawer element is no longer accessible via viewer.canvas.firstChild but via viewer.drawersContainer.firstChild or viewer.drawer.canvas.
|
||||||
|
* The overlays elements are no longer accessible via viewer.canvas.childNodes but via viewer.overlaysContainer.childNodes or viewer.currentOverlays[i].element.
|
||||||
|
* BREAKING CHANGE: Pseudo full screen mode on IE<11 using activex has been dropped. OpenSeadragon will run in full page if full screen mode is requested.
|
||||||
|
* BREAKING CHANGE: MouseTracker touch pinch gestures are no longer converted to scroll events. MouseTracker.pinchHandler should be used instead. (#369)
|
||||||
|
* DEPRECATION: overlay functions have been moved from Drawer to Viewer (#331)
|
||||||
|
* DEPRECATION: OpenSeadragon.cancelFullScreen has been renamed OpenSeadragon.exitFullScreen (#358)
|
||||||
|
* DEPRECATION: The 'isTouchEvent' property passed in MouseTracker events is deprecated and has been replaced with 'pointerType', which is a String value "mouse", "touch", "pen", etc. to support multiple simultaneous pointing devices (#369)
|
||||||
|
* DEPRECATION: The 'buttonDownAny' property passed in MouseTracker enter and exit events (enterHandler/exitHandler) is deprecated and has been replaced with 'buttons', which indicates the button(s) currently pressed (#369)
|
||||||
|
* DEPRECATION: The 'buttonDownAny' property passed in Viewer's 'container-enter' and 'container-exit' events is deprecated and has been replaced with 'buttons', which indicates the button(s) currently pressed (#369)
|
||||||
|
* Added layers support. Multiple images can now been displayed on top of each other with transparency via the Viewer.addLayer method (#298)
|
||||||
|
* Improved overlay functions (#331)
|
||||||
|
* Fixed: Nav button highlight states aren't quite aligned on Firefox (#303)
|
||||||
|
* Added ControlAnchor options for default controls (#304)
|
||||||
|
* Enabled basic cross-domain tile loading without tainting canvas (works in Chrome and Firefox) (#308)
|
||||||
|
* Added crossOriginPolicy drawer configuration to enable or disable CORS image requests (#364)
|
||||||
|
* Disabled CORS by default (#377)
|
||||||
|
* Added a ControlAnchor.ABSOLUTE enumeration. Enables absolute positioning of control elements in the viewer (#310)
|
||||||
|
* Added a 'navigator-scroll' event to Navigator. Fired when mousewheel events occur in the navigator (#310)
|
||||||
|
* Added a navigatorMaintainSizeRatio option. If set to true, the navigator minimap resizes when the viewer element is resized (#310)
|
||||||
|
* Added 'ABSOLUTE' as a navigatorPosition option, along with corresponding navigatorTop, navigatorLeft options. Allows the navigator minimap to be placed anywhere in the viewer (#310)
|
||||||
|
* Enhanced the navigatorTop, navigatorLeft, navigatorHeight, and navigatorWidth options to allow a number for pixel units or a string for other element units (%, em, etc.) (#310)
|
||||||
|
* Additional enhancements for IIIF support (#315)
|
||||||
|
* Fixed: Setting degrees in Viewer constructor has no effect (#336)
|
||||||
|
* Added pre-draw event for tiles to allow applications to alter the image (#348)
|
||||||
|
* Added optional Rotate Left/Right buttons to standard controls (#341)
|
||||||
|
* Added optimization for large numbers of overlays: `checkResize = false` option for OpenSeadragon.Overlay (#365)
|
||||||
|
* Updated full screen API, adding support for Opera and IE11 and allowing keyboard input in Chrome (#358)
|
||||||
|
* Various fixes to bring OpenSeadragon into W3C compliance (#375)
|
||||||
|
* Added separate flags for turning off each of the nav buttons (#376)
|
||||||
|
* Added support for query parameters in DZI tileSource URL (#378)
|
||||||
|
* Enhanced MouseTracker for multi-touch (#369)
|
||||||
|
* Added support for tracking multiple touch-points on multiple/simultaneous pointing devices
|
||||||
|
* Added support for the W3C Pointer Events event model. Enables touch/multi-touch on IE10+
|
||||||
|
* Added a dragEndHandler event callback, called when a drag gesture ends
|
||||||
|
* Added a pinchHandler event callback, called as a pinch gesture (2 touch points) is occurring
|
||||||
|
* Added real-time velocity (speed and direction) tracking to drag operations. 'speed' and 'direction' values are passed in the dragHandler and dragEndHandler event data
|
||||||
|
* Enhanced Viewer for multi-touch (#369)
|
||||||
|
* Added pinch zoom with the new MouseTracker pinchHandler. The 'pan' and 'zoom' Viewer events can be used to detect changes resulting in pinch gestures
|
||||||
|
* Added a "canvas-pinch" event fired by the pinch event handler
|
||||||
|
* Added flick gesture with the new MouseTracker dragEndHandler
|
||||||
|
* Added a "canvas-drag-end" event fired by the drag-end event handler
|
||||||
|
* Added a GestureSettings class for per-device gesture options. Currently has settings to enable/disable zoom-on-scroll, zoom-on-pinch, zoom-on-click, and flick gesture settings.
|
||||||
|
* Added GestureSettings objects for mouse, touch, and pen devices to the Viewer options giving users the ability to customize gesture handling in the viewer
|
||||||
|
* Added velocity (speed and direction) properties to the "canvas-drag" event
|
||||||
|
* Added double-click gesture detection to MouseTracker with corresponding dblClickHandler event callback (#392)
|
||||||
|
* Added zoom on double-click feature to Viewer, with corresponding dblClickToZoom option added to the GestureSettings class (#392)
|
||||||
|
* Made it possible to run OpenSeadragon from local filesystem on some browsers (#379)
|
||||||
|
|
||||||
|
1.0.0:
|
||||||
|
|
||||||
|
NOTE: This version has a number of breaking changes to the API, mostly in event handling. See below.
|
||||||
|
|
||||||
|
* BREAKING CHANGE: All EventSource and MouseTracker event handler method signatures changed to 'handlerMethod(event)' where event == { eventSource, userData, ... } (#251) (Also fixes #23, #224, #239)
|
||||||
|
* The new eventSource property in the event object replaces the old eventSource parameter that was passed to handler methods.
|
||||||
|
* Where the event object duplicated the eventSource value, those properties have been removed. This affects the following events:
|
||||||
|
* All Button events - 'button' property removed
|
||||||
|
* All Viewer (Viewer, Drawer, Viewport) events - 'viewer' property removed
|
||||||
|
* BREAKING CHANGE: Renamed EventHandler to EventSource (#225)
|
||||||
|
* BREAKING CHANGE: Event names changed for consistency: changed to lower case, compound names hyphenated, and "on" prefixes removed (#226):
|
||||||
|
* Viewer "animationstart" changed to "animation-start"
|
||||||
|
* Viewer "animationfinish" changed to "animation-finish"
|
||||||
|
* Button "onPress" changed to "press"
|
||||||
|
* Button "onRelease" changed to "release"
|
||||||
|
* Button "onClick" changed to "click"
|
||||||
|
* Button "onEnter" changed to "enter"
|
||||||
|
* Button "onExit" changed to "exit"
|
||||||
|
* Button "onFocus" changed to "focus"
|
||||||
|
* Button "onBlur" changed to "blur"
|
||||||
|
* BREAKING CHANGE: Numerous improvements to fullPage/fullScreen (#256):
|
||||||
|
* Retains zoom/pan position better when switching into and out of fullPage.
|
||||||
|
* Retains scroll position when switching back out.
|
||||||
|
* More resilient to styling variations on the page.
|
||||||
|
* setFullPage no longer automatically engages fullScreen; there's now a separate setFullScreen.
|
||||||
|
* 'fullpage' event is now 'full-page'.
|
||||||
|
* The `fullpage` property of the 'full-page' event is now `fullPage`.
|
||||||
|
* There is now a 'full-screen' event with a `fullScreen` property (true if it has gone to full screen).
|
||||||
|
* There are now 'pre-full-page' and 'pre-full-screen' events that include a `preventDefaultAction` property you can set in your handler to cancel. They also have `fullPage` and `fullScreen` properties respectively, to indicate if they are going into or out of the mode.
|
||||||
|
* BREAKING CHANGE: Removed the 'onPageChange' callback from the viewer options. Viewer.goToPage() now raises the 'page' event only (#285)
|
||||||
|
* Major documentation improvements (#281)
|
||||||
|
* MouseTracker now passes the original event objects to its handler methods (#23)
|
||||||
|
* MouseTracker now supports an optional 'moveHandler' method for tracking mousemove events (#215)
|
||||||
|
* Added stopHandler to MouseTracker. (#262)
|
||||||
|
* Fixed: Element-relative mouse coordinates now correct if the element and/or page is scrolled (using new OpenSeadragon.getElementOffset() method) (#131)
|
||||||
|
* Fixed: Pinch zoom event issue, regressive issue from previous event system changes (#244)
|
||||||
|
* Added IIIF Image API 1.1 Tile Source (#230)
|
||||||
|
* IIIF 1.0 now uses pixel based syntax (#249)
|
||||||
|
* Fixed: Touch event issue where no canvas-click events were being raised (#240)
|
||||||
|
* Check that zoom reference point is valid before using it in zoomTo and zoomBy (#247)
|
||||||
|
* Added a number of easier coordinate conversion methods to viewport (#243)
|
||||||
|
* Added the ability to create a viewer and start at a specified page (#252)
|
||||||
|
* Fixed image resolve issue with collection mode (#255)
|
||||||
|
* DOM events are now passed through as 'event.originalEvent' in viewer and button events where appropriate. (#257) Affects the following events:
|
||||||
|
* Viewer: 'canvas-release', 'canvas-click', 'canvas-drag', 'canvas-scroll', 'container-enter', 'container-exit', 'container-release'
|
||||||
|
* Button: 'enter', 'exit', 'press', 'release', 'focus', 'blur', 'click'
|
||||||
|
* Fixed: IE 10 not reading DZI file correctly in certain circumstances (#218)
|
||||||
|
* Added support for the 'wheel' DOM mousewheel event (#261)
|
||||||
|
* Fix for non-canvas tile rendering at large size (#264)
|
||||||
|
* Drawer now uses an HTML5 canvas element whenever it's available. Can be overridden with the Viewer.useCanvas option (#191)
|
||||||
|
* Added a boolean preventDefaultAction property (default false) to the event object passed to MouseTracker handler methods. (#270) Implemented in the following MouseTracker subscribers:
|
||||||
|
* Viewer.keyboardCommandArea.innerTracker.focusHandler: preventDefaultAction == true prevents scrolling viewer into view
|
||||||
|
* Viewer.keyboardCommandArea.innerTracker.keyHandler: preventDefaultAction == true prevents viewer keyboard navigation
|
||||||
|
* Viewer.innerTracker.clickHandler: preventDefaultAction == true prevents viewer zoom on click
|
||||||
|
* Viewer.innerTracker.dragHandler: preventDefaultAction == true prevents viewer panning with mouse/touch
|
||||||
|
* Viewer.innerTracker.scrollHandler: preventDefaultAction == true prevents viewer zooming on mousewheel/pinch
|
||||||
|
* Fixed: IE8 error with custom buttons - "Object doesn't support this action" (#279)
|
||||||
|
* Support IIIF servers that don't report tile dimensions (#286)
|
||||||
|
* Added an autoResize option. Default is true. When set to false, the viewer takes no action when its container element is resized. (#291)
|
||||||
|
* Added a static 'version' property to OpenSeadragon. Useful for plugins that require specific OpenSeadragon versions. (#292)
|
||||||
|
|
||||||
|
0.9.131:
|
||||||
|
|
||||||
|
* Fixed: canvas-click event shouldn't fire as you drag (#198)
|
||||||
|
* Fixed: LegacyTileSource doesn't fail gracefully when no supported file formats are found (#202)
|
||||||
|
* Added an optional userData argument to EventHandler.addHandler() which is passed unchanged to the handler method (#203)
|
||||||
|
* Fixed AJAX error reporting on IE8 (#208)
|
||||||
|
* Added viewportToImageRectangle method, and updated imageToViewportRectangle, imageToViewportCoordinates, and viewportToImageCoordinates to be more flexible with params (#212)
|
||||||
|
* Fixed: Viewer is not responsive (css) after returning from full screen (#222)
|
||||||
|
|
||||||
|
0.9.130:
|
||||||
|
|
||||||
|
* Added partial support for rotation (just 90 degree increments for now). (#185)
|
||||||
|
* Hiding and restoring broke the viewer; fixed (#177)
|
||||||
|
* You can now provide an onDraw function for overlays to do custom overlay manipulation (#160)
|
||||||
|
* Added a destroy function on the viewer to clean up and remove elements (#179)
|
||||||
|
* Fixed: navigatorPosition option corrected. (#163)
|
||||||
|
* OpenSeadragon.now() returned undefined the first time; fixed
|
||||||
|
* onTouchEnd did not call the correct mouse up handler; fixed (#159)
|
||||||
|
* Touch events no longer capture mouse (was causing issues on devices that support both) (#168)
|
||||||
|
* Clicking on a button control no longer refreshes page (#184)
|
||||||
|
* Drawer now works when the page is rtl (#187)
|
||||||
|
* Fixed a situation that could throw errors in touch handling (#188)
|
||||||
|
|
||||||
|
0.9.129:
|
||||||
|
|
||||||
|
* Fixed: navigator image not updating when base zoom image is changed (#147)
|
||||||
|
* Fixed tile rendering issue at lower zoom levels with the IIIF TileSource (#55)
|
||||||
|
* On IE, ajax errors would cause an exception to be thrown; fixed (#144)
|
||||||
|
* Faster and more consistent millisecond getter (#138)
|
||||||
|
* Fixed an error when using navPrevNextWrap on single images (#135)
|
||||||
|
* Various fixes to our timer handling (#133)
|
||||||
|
* Now generating source map for openseadragon.min.js (#51)
|
||||||
|
* Fix for calculating overlay width / height (#142)
|
||||||
|
* JSHint tidying (#136)
|
||||||
|
* Improved Ajax method (#149)
|
||||||
|
* Overhauled AJAX error reporting (#151)
|
||||||
|
|
||||||
|
0.9.128:
|
||||||
|
|
||||||
|
* The navigator is now off by default (#102)
|
||||||
|
* Reverted minPixelRatio to 0.5 for better quality (#116)
|
||||||
|
* Sometimes tiles wouldn't resolve if you used the blendTime option; fixed. (#95)
|
||||||
|
* You can now choose to have previous and next buttons wrap using the config.navPrevNextWrap. (#114)
|
||||||
|
* You can now specify an ID for a div to hold the navigator (#46)
|
||||||
|
* You can now click in the navigator to go to a new location (#46)
|
||||||
|
* Keyboard handling is now done in the viewer rather than navigator (#46)
|
||||||
|
* Additional navigator fixes (#46)
|
||||||
|
* Drawer events now fire properly (#94)
|
||||||
|
* Fixed an error in EventHandler.removeHandler() (#48)
|
||||||
|
* Better requestAnimationFrame detection on older Firefox (#103)
|
||||||
|
* More efficient navigator loading (#115)
|
||||||
|
* Simplified element opacity setting implementation (#123)
|
||||||
|
|
||||||
|
0.9.127:
|
||||||
|
|
||||||
|
* Fixed a problem with getString when the string property is a sub-property. (#64)
|
||||||
|
* Fixed: Tooltips for Navigation Controls not displaying (#63)
|
||||||
|
* Cleaned up some diagnostic code that was broken.
|
||||||
|
* Added fullpage class to viewer element when in fullpage mode (#61)
|
||||||
|
* Reverted to original New BSD license; cleaned up license declarations (#89)
|
||||||
|
|
||||||
|
0.9.126:
|
||||||
|
|
||||||
|
* DZI JSONp was broken; fixed.
|
||||||
|
|
||||||
|
0.9.125:
|
||||||
|
|
||||||
|
* Fully deprecated OpenSeadragon.createFromDZI, safely deprecated Viewer.openTileSource and
|
||||||
|
Viewer.openDZI to use Viewer.open internally. (#53 & #54).
|
||||||
|
* Full page bug fix for when viewer is child of document body (#43).
|
||||||
|
* Overlays for DZI bug fix (#45).
|
||||||
|
* DziTileSource: avoid changing relative paths (#56).
|
||||||
|
* Fix typo in preserveViewport handling (#77).
|
||||||
|
* Fix updateMulti timer leak after multiple Viewer.open() calls (#76).
|
||||||
|
* Minor documentation fixes.
|
||||||
|
|
||||||
|
0.9.124:
|
||||||
|
|
||||||
|
* Performance enhancements.
|
||||||
|
|
||||||
|
|
||||||
|
0.9.123:
|
||||||
|
|
||||||
|
* Real fullscreen support.
|
||||||
|
|
||||||
|
|
||||||
|
0.9.122:
|
||||||
|
|
||||||
|
* Performance enhancements.
|
||||||
|
|
||||||
|
|
||||||
|
0.9.121:
|
||||||
|
|
||||||
|
* Touch pan now works on Android.
|
||||||
|
* Pinch zoom is better on all devices.
|
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 4.8 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 977 B |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 1.1 KiB |
|
@ -0,0 +1,493 @@
|
||||||
|
/*
|
||||||
|
* This software was developed at the National Institute of Standards and
|
||||||
|
* Technology by employees of the Federal Government in the course of
|
||||||
|
* their official duties. Pursuant to title 17 Section 105 of the United
|
||||||
|
* States Code this software is not subject to copyright protection and is
|
||||||
|
* in the public domain. This software is an experimental system. NIST assumes
|
||||||
|
* no responsibility whatsoever for its use by other parties, and makes no
|
||||||
|
* guarantees, expressed or implied, about its quality, reliability, or
|
||||||
|
* any other characteristic. We would appreciate acknowledgement if the
|
||||||
|
* software is used.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Antoine Vandecreme <antoine.vandecreme@nist.gov>
|
||||||
|
*/
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var $ = window.OpenSeadragon;
|
||||||
|
if (!$) {
|
||||||
|
$ = require('openseadragon');
|
||||||
|
if (!$) {
|
||||||
|
throw new Error('OpenSeadragon is missing.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Requires OpenSeadragon >=2.1
|
||||||
|
if (!$.version || $.version.major < 2 ||
|
||||||
|
$.version.major === 2 && $.version.minor < 1) {
|
||||||
|
throw new Error(
|
||||||
|
'Filtering plugin requires OpenSeadragon version >= 2.1');
|
||||||
|
}
|
||||||
|
|
||||||
|
$.Viewer.prototype.setFilterOptions = function(options) {
|
||||||
|
if (!this.filterPluginInstance) {
|
||||||
|
options = options || {};
|
||||||
|
options.viewer = this;
|
||||||
|
this.filterPluginInstance = new $.FilterPlugin(options);
|
||||||
|
} else {
|
||||||
|
setOptions(this.filterPluginInstance, options);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class FilterPlugin
|
||||||
|
* @param {Object} options The options
|
||||||
|
* @param {OpenSeadragon.Viewer} options.viewer The viewer to attach this
|
||||||
|
* plugin to.
|
||||||
|
* @param {String} [options.loadMode='async'] Set to sync to have the filters
|
||||||
|
* applied synchronously. It will only work if the filters are all synchronous.
|
||||||
|
* Note that depending on how complex the filters are, it may also hang the browser.
|
||||||
|
* @param {Object[]} options.filters The filters to apply to the images.
|
||||||
|
* @param {OpenSeadragon.TiledImage[]} options.filters[x].items The tiled images
|
||||||
|
* on which to apply the filter.
|
||||||
|
* @param {function|function[]} options.filters[x].processors The processing
|
||||||
|
* function(s) to apply to the images. The parameters of this function are
|
||||||
|
* the context to modify and a callback to call upon completion.
|
||||||
|
*/
|
||||||
|
$.FilterPlugin = function(options) {
|
||||||
|
options = options || {};
|
||||||
|
if (!options.viewer) {
|
||||||
|
throw new Error('A viewer must be specified.');
|
||||||
|
}
|
||||||
|
var self = this;
|
||||||
|
this.viewer = options.viewer;
|
||||||
|
|
||||||
|
this.viewer.addHandler('tile-loaded', tileLoadedHandler);
|
||||||
|
this.viewer.addHandler('tile-drawing', tileDrawingHandler);
|
||||||
|
|
||||||
|
// filterIncrement allows to determine whether a tile contains the
|
||||||
|
// latest filters results.
|
||||||
|
this.filterIncrement = 0;
|
||||||
|
|
||||||
|
setOptions(this, options);
|
||||||
|
|
||||||
|
|
||||||
|
function tileLoadedHandler(event) {
|
||||||
|
var processors = getFiltersProcessors(self, event.tiledImage);
|
||||||
|
if (processors.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var tile = event.tile;
|
||||||
|
var image = event.data;
|
||||||
|
if (image !== null && image !== undefined) {
|
||||||
|
var canvas = window.document.createElement('canvas');
|
||||||
|
canvas.width = image.width;
|
||||||
|
canvas.height = image.height;
|
||||||
|
var context = canvas.getContext('2d');
|
||||||
|
context.drawImage(image, 0, 0);
|
||||||
|
tile._renderedContext = context;
|
||||||
|
var callback = event.getCompletionCallback();
|
||||||
|
applyFilters(context, processors, callback);
|
||||||
|
tile._filterIncrement = self.filterIncrement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function applyFilters(context, filtersProcessors, callback) {
|
||||||
|
if (callback) {
|
||||||
|
var currentIncrement = self.filterIncrement;
|
||||||
|
var callbacks = [];
|
||||||
|
for (var i = 0; i < filtersProcessors.length - 1; i++) {
|
||||||
|
(function(i) {
|
||||||
|
callbacks[i] = function() {
|
||||||
|
// If the increment has changed, stop the computation
|
||||||
|
// chain immediately.
|
||||||
|
if (self.filterIncrement !== currentIncrement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
filtersProcessors[i + 1](context, callbacks[i + 1]);
|
||||||
|
};
|
||||||
|
})(i);
|
||||||
|
}
|
||||||
|
callbacks[filtersProcessors.length - 1] = function() {
|
||||||
|
// If the increment has changed, do not call the callback.
|
||||||
|
// (We don't want OSD to draw an outdated tile in the canvas).
|
||||||
|
if (self.filterIncrement !== currentIncrement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
filtersProcessors[0](context, callbacks[0]);
|
||||||
|
} else {
|
||||||
|
for (var i = 0; i < filtersProcessors.length; i++) {
|
||||||
|
filtersProcessors[i](context, function() {
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function tileDrawingHandler(event) {
|
||||||
|
var tile = event.tile;
|
||||||
|
var rendered = event.rendered;
|
||||||
|
if (rendered._filterIncrement === self.filterIncrement) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var processors = getFiltersProcessors(self, event.tiledImage);
|
||||||
|
if (processors.length === 0) {
|
||||||
|
if (rendered._originalImageData) {
|
||||||
|
// Restore initial data.
|
||||||
|
rendered.putImageData(rendered._originalImageData, 0, 0);
|
||||||
|
delete rendered._originalImageData;
|
||||||
|
}
|
||||||
|
rendered._filterIncrement = self.filterIncrement;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rendered._originalImageData) {
|
||||||
|
// The tile has been previously filtered (by another filter),
|
||||||
|
// restore it first.
|
||||||
|
rendered.putImageData(rendered._originalImageData, 0, 0);
|
||||||
|
} else {
|
||||||
|
rendered._originalImageData = rendered.getImageData(
|
||||||
|
0, 0, rendered.canvas.width, rendered.canvas.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tile._renderedContext) {
|
||||||
|
if (tile._filterIncrement === self.filterIncrement) {
|
||||||
|
var imgData = tile._renderedContext.getImageData(0, 0,
|
||||||
|
tile._renderedContext.canvas.width,
|
||||||
|
tile._renderedContext.canvas.height);
|
||||||
|
rendered.putImageData(imgData, 0, 0);
|
||||||
|
delete tile._renderedContext;
|
||||||
|
delete tile._filterIncrement;
|
||||||
|
rendered._filterIncrement = self.filterIncrement;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
delete tile._renderedContext;
|
||||||
|
delete tile._filterIncrement;
|
||||||
|
}
|
||||||
|
applyFilters(rendered, processors);
|
||||||
|
rendered._filterIncrement = self.filterIncrement;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function setOptions(instance, options) {
|
||||||
|
options = options || {};
|
||||||
|
var filters = options.filters;
|
||||||
|
instance.filters = !filters ? [] :
|
||||||
|
$.isArray(filters) ? filters : [filters];
|
||||||
|
for (var i = 0; i < instance.filters.length; i++) {
|
||||||
|
var filter = instance.filters[i];
|
||||||
|
if (!filter.processors) {
|
||||||
|
throw new Error('Filter processors must be specified.');
|
||||||
|
}
|
||||||
|
filter.processors = $.isArray(filter.processors) ?
|
||||||
|
filter.processors : [filter.processors];
|
||||||
|
}
|
||||||
|
instance.filterIncrement++;
|
||||||
|
|
||||||
|
if (options.loadMode === 'sync') {
|
||||||
|
instance.viewer.forceRedraw();
|
||||||
|
} else {
|
||||||
|
var itemsToReset = [];
|
||||||
|
for (var i = 0; i < instance.filters.length; i++) {
|
||||||
|
var filter = instance.filters[i];
|
||||||
|
if (!filter.items) {
|
||||||
|
itemsToReset = getAllItems(instance.viewer.world);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ($.isArray(filter.items)) {
|
||||||
|
for (var j = 0; j < filter.items.length; j++) {
|
||||||
|
addItemToReset(filter.items[j], itemsToReset);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
addItemToReset(filter.items, itemsToReset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (var i = 0; i < itemsToReset.length; i++) {
|
||||||
|
itemsToReset[i].reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addItemToReset(item, itemsToReset) {
|
||||||
|
if (itemsToReset.indexOf(item) >= 0) {
|
||||||
|
throw new Error('An item can not have filters ' +
|
||||||
|
'assigned multiple times.');
|
||||||
|
}
|
||||||
|
itemsToReset.push(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAllItems(world) {
|
||||||
|
var result = [];
|
||||||
|
for (var i = 0; i < world.getItemCount(); i++) {
|
||||||
|
result.push(world.getItemAt(i));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFiltersProcessors(instance, item) {
|
||||||
|
if (instance.filters.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
var globalProcessors = null;
|
||||||
|
for (var i = 0; i < instance.filters.length; i++) {
|
||||||
|
var filter = instance.filters[i];
|
||||||
|
if (!filter.items) {
|
||||||
|
globalProcessors = filter.processors;
|
||||||
|
} else if (filter.items === item ||
|
||||||
|
$.isArray(filter.items) && filter.items.indexOf(item) >= 0) {
|
||||||
|
return filter.processors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return globalProcessors ? globalProcessors : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$.Filters = {
|
||||||
|
THRESHOLDING: function(threshold) {
|
||||||
|
if (threshold < 0 || threshold > 255) {
|
||||||
|
throw new Error('Threshold must be between 0 and 255.');
|
||||||
|
}
|
||||||
|
return function(context, callback) {
|
||||||
|
var imgData = context.getImageData(
|
||||||
|
0, 0, context.canvas.width, context.canvas.height);
|
||||||
|
var pixels = imgData.data;
|
||||||
|
for (var i = 0; i < pixels.length; i += 4) {
|
||||||
|
var r = pixels[i];
|
||||||
|
var g = pixels[i + 1];
|
||||||
|
var b = pixels[i + 2];
|
||||||
|
var v = (r + g + b) / 3;
|
||||||
|
pixels[i] = pixels[i + 1] = pixels[i + 2] =
|
||||||
|
v < threshold ? 0 : 255;
|
||||||
|
}
|
||||||
|
context.putImageData(imgData, 0, 0);
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
},
|
||||||
|
BRIGHTNESS: function(adjustment) {
|
||||||
|
if (adjustment < -255 || adjustment > 255) {
|
||||||
|
throw new Error(
|
||||||
|
'Brightness adjustment must be between -255 and 255.');
|
||||||
|
}
|
||||||
|
var precomputedBrightness = [];
|
||||||
|
for (var i = 0; i < 256; i++) {
|
||||||
|
precomputedBrightness[i] = i + adjustment;
|
||||||
|
}
|
||||||
|
return function(context, callback) {
|
||||||
|
var imgData = context.getImageData(
|
||||||
|
0, 0, context.canvas.width, context.canvas.height);
|
||||||
|
var pixels = imgData.data;
|
||||||
|
for (var i = 0; i < pixels.length; i += 4) {
|
||||||
|
pixels[i] = precomputedBrightness[pixels[i]];
|
||||||
|
pixels[i + 1] = precomputedBrightness[pixels[i + 1]];
|
||||||
|
pixels[i + 2] = precomputedBrightness[pixels[i + 2]];
|
||||||
|
}
|
||||||
|
context.putImageData(imgData, 0, 0);
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
},
|
||||||
|
CONTRAST: function(adjustment) {
|
||||||
|
if (adjustment < 0) {
|
||||||
|
throw new Error('Contrast adjustment must be positive.');
|
||||||
|
}
|
||||||
|
var precomputedContrast = [];
|
||||||
|
for (var i = 0; i < 256; i++) {
|
||||||
|
precomputedContrast[i] = i * adjustment;
|
||||||
|
}
|
||||||
|
return function(context, callback) {
|
||||||
|
var imgData = context.getImageData(
|
||||||
|
0, 0, context.canvas.width, context.canvas.height);
|
||||||
|
var pixels = imgData.data;
|
||||||
|
for (var i = 0; i < pixels.length; i += 4) {
|
||||||
|
pixels[i] = precomputedContrast[pixels[i]];
|
||||||
|
pixels[i + 1] = precomputedContrast[pixels[i + 1]];
|
||||||
|
pixels[i + 2] = precomputedContrast[pixels[i + 2]];
|
||||||
|
}
|
||||||
|
context.putImageData(imgData, 0, 0);
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
},
|
||||||
|
GAMMA: function(adjustment) {
|
||||||
|
if (adjustment < 0) {
|
||||||
|
throw new Error('Gamma adjustment must be positive.');
|
||||||
|
}
|
||||||
|
var precomputedGamma = [];
|
||||||
|
for (var i = 0; i < 256; i++) {
|
||||||
|
precomputedGamma[i] = Math.pow(i / 255, adjustment) * 255;
|
||||||
|
}
|
||||||
|
return function(context, callback) {
|
||||||
|
var imgData = context.getImageData(
|
||||||
|
0, 0, context.canvas.width, context.canvas.height);
|
||||||
|
var pixels = imgData.data;
|
||||||
|
for (var i = 0; i < pixels.length; i += 4) {
|
||||||
|
pixels[i] = precomputedGamma[pixels[i]];
|
||||||
|
pixels[i + 1] = precomputedGamma[pixels[i + 1]];
|
||||||
|
pixels[i + 2] = precomputedGamma[pixels[i + 2]];
|
||||||
|
}
|
||||||
|
context.putImageData(imgData, 0, 0);
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
},
|
||||||
|
GREYSCALE: function() {
|
||||||
|
return function(context, callback) {
|
||||||
|
var imgData = context.getImageData(
|
||||||
|
0, 0, context.canvas.width, context.canvas.height);
|
||||||
|
var pixels = imgData.data;
|
||||||
|
for (var i = 0; i < pixels.length; i += 4) {
|
||||||
|
var val = (pixels[i] + pixels[i + 1] + pixels[i + 2]) / 3;
|
||||||
|
pixels[i] = val;
|
||||||
|
pixels[i + 1] = val;
|
||||||
|
pixels[i + 2] = val;
|
||||||
|
}
|
||||||
|
context.putImageData(imgData, 0, 0);
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
},
|
||||||
|
INVERT: function() {
|
||||||
|
var precomputedInvert = [];
|
||||||
|
for (var i = 0; i < 256; i++) {
|
||||||
|
precomputedInvert[i] = 255 - i;
|
||||||
|
}
|
||||||
|
return function(context, callback) {
|
||||||
|
var imgData = context.getImageData(
|
||||||
|
0, 0, context.canvas.width, context.canvas.height);
|
||||||
|
var pixels = imgData.data;
|
||||||
|
for (var i = 0; i < pixels.length; i += 4) {
|
||||||
|
pixels[i] = precomputedInvert[pixels[i]];
|
||||||
|
pixels[i + 1] = precomputedInvert[pixels[i + 1]];
|
||||||
|
pixels[i + 2] = precomputedInvert[pixels[i + 2]];
|
||||||
|
}
|
||||||
|
context.putImageData(imgData, 0, 0);
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
},
|
||||||
|
MORPHOLOGICAL_OPERATION: function(kernelSize, comparator) {
|
||||||
|
if (kernelSize % 2 === 0) {
|
||||||
|
throw new Error('The kernel size must be an odd number.');
|
||||||
|
}
|
||||||
|
var kernelHalfSize = Math.floor(kernelSize / 2);
|
||||||
|
|
||||||
|
if (!comparator) {
|
||||||
|
throw new Error('A comparator must be defined.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return function(context, callback) {
|
||||||
|
var width = context.canvas.width;
|
||||||
|
var height = context.canvas.height;
|
||||||
|
var imgData = context.getImageData(0, 0, width, height);
|
||||||
|
var originalPixels = context.getImageData(0, 0, width, height)
|
||||||
|
.data;
|
||||||
|
var offset;
|
||||||
|
|
||||||
|
for (var y = 0; y < height; y++) {
|
||||||
|
for (var x = 0; x < width; x++) {
|
||||||
|
offset = (y * width + x) * 4;
|
||||||
|
var r = originalPixels[offset];
|
||||||
|
var g = originalPixels[offset + 1];
|
||||||
|
var b = originalPixels[offset + 2];
|
||||||
|
for (var j = 0; j < kernelSize; j++) {
|
||||||
|
for (var i = 0; i < kernelSize; i++) {
|
||||||
|
var pixelX = x + i - kernelHalfSize;
|
||||||
|
var pixelY = y + j - kernelHalfSize;
|
||||||
|
if (pixelX >= 0 && pixelX < width &&
|
||||||
|
pixelY >= 0 && pixelY < height) {
|
||||||
|
offset = (pixelY * width + pixelX) * 4;
|
||||||
|
r = comparator(originalPixels[offset], r);
|
||||||
|
g = comparator(
|
||||||
|
originalPixels[offset + 1], g);
|
||||||
|
b = comparator(
|
||||||
|
originalPixels[offset + 2], b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
imgData.data[offset] = r;
|
||||||
|
imgData.data[offset + 1] = g;
|
||||||
|
imgData.data[offset + 2] = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
context.putImageData(imgData, 0, 0);
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
},
|
||||||
|
CONVOLUTION: function(kernel) {
|
||||||
|
if (!$.isArray(kernel)) {
|
||||||
|
throw new Error('The kernel must be an array.');
|
||||||
|
}
|
||||||
|
var kernelSize = Math.sqrt(kernel.length);
|
||||||
|
if ((kernelSize + 1) % 2 !== 0) {
|
||||||
|
throw new Error('The kernel must be a square matrix with odd' +
|
||||||
|
'width and height.');
|
||||||
|
}
|
||||||
|
var kernelHalfSize = (kernelSize - 1) / 2;
|
||||||
|
|
||||||
|
return function(context, callback) {
|
||||||
|
var width = context.canvas.width;
|
||||||
|
var height = context.canvas.height;
|
||||||
|
var imgData = context.getImageData(0, 0, width, height);
|
||||||
|
var originalPixels = context.getImageData(0, 0, width, height)
|
||||||
|
.data;
|
||||||
|
var offset;
|
||||||
|
|
||||||
|
for (var y = 0; y < height; y++) {
|
||||||
|
for (var x = 0; x < width; x++) {
|
||||||
|
var r = 0;
|
||||||
|
var g = 0;
|
||||||
|
var b = 0;
|
||||||
|
for (var j = 0; j < kernelSize; j++) {
|
||||||
|
for (var i = 0; i < kernelSize; i++) {
|
||||||
|
var pixelX = x + i - kernelHalfSize;
|
||||||
|
var pixelY = y + j - kernelHalfSize;
|
||||||
|
if (pixelX >= 0 && pixelX < width &&
|
||||||
|
pixelY >= 0 && pixelY < height) {
|
||||||
|
offset = (pixelY * width + pixelX) * 4;
|
||||||
|
var weight = kernel[j * kernelSize + i];
|
||||||
|
r += originalPixels[offset] * weight;
|
||||||
|
g += originalPixels[offset + 1] * weight;
|
||||||
|
b += originalPixels[offset + 2] * weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
offset = (y * width + x) * 4;
|
||||||
|
imgData.data[offset] = r;
|
||||||
|
imgData.data[offset + 1] = g;
|
||||||
|
imgData.data[offset + 2] = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
context.putImageData(imgData, 0, 0);
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
},
|
||||||
|
COLORMAP: function(cmap, ctr) {
|
||||||
|
var resampledCmap = cmap.slice(0);
|
||||||
|
var diff = 255 - ctr;
|
||||||
|
for(var i = 0; i < 256; i++) {
|
||||||
|
var position = 0;
|
||||||
|
if(i > ctr) {
|
||||||
|
position = Math.min((i - ctr) / diff * 128 + 128,255) | 0;
|
||||||
|
}else{
|
||||||
|
position = Math.max(0, i / (ctr / 128)) | 0;
|
||||||
|
}
|
||||||
|
resampledCmap[i] = cmap[position];
|
||||||
|
}
|
||||||
|
return function(context, callback) {
|
||||||
|
var imgData = context.getImageData(
|
||||||
|
0, 0, context.canvas.width, context.canvas.height);
|
||||||
|
var pxl = imgData.data;
|
||||||
|
for (var i = 0; i < pxl.length; i += 4) {
|
||||||
|
var v = (pxl[i] + pxl[i + 1] + pxl[i + 2]) / 3 | 0;
|
||||||
|
var c = resampledCmap[v];
|
||||||
|
pxl[i] = c[0];
|
||||||
|
pxl[i + 1] = c[1];
|
||||||
|
pxl[i + 2] = c[2];
|
||||||
|
}
|
||||||
|
context.putImageData(imgData, 0, 0);
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}());
|
|
@ -0,0 +1,147 @@
|
||||||
|
/**
|
||||||
|
* @file OpenSeadragon plugin that allows for adjustment of zoom speed based upon the speed that the user scrolls the mouse wheel
|
||||||
|
* @author Bassil Virk
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function($){
|
||||||
|
|
||||||
|
$.Viewer.prototype.smartScrollZoom = function (options) {
|
||||||
|
if (!this.smartScrollZoomInstance) {
|
||||||
|
options = options || {};
|
||||||
|
options.viewer = this;
|
||||||
|
this.smartScrollZoomInstance = new $.SmartScrollZoom(options);
|
||||||
|
} else {
|
||||||
|
this.smartScrollZoomInstance.setOptions(options);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class SmartScrollZoom
|
||||||
|
* @classdesc Changes zoom speed based on scrolling speed
|
||||||
|
* @memberof OpenSeadragon
|
||||||
|
* @param {Object} options
|
||||||
|
* @param {Number} options.timeThreshold - Amount of time, in milliseconds, that the minimum number of scrolls must occur within
|
||||||
|
* before logic begins
|
||||||
|
* @param {Number} options.minScrolls - Required number of consecutive scrolls that must take place within the specified time
|
||||||
|
* threshold of each other before logic begins
|
||||||
|
* @param {Number} options.minZoomPerScroll - Minimum factor to zoom by with a single scroll. Setting this to 1 will affectively
|
||||||
|
* result in no zoom while logic is not being executing
|
||||||
|
* @param {Number} options.maxZoomPerScroll - Maximum zoom factor that can be reached
|
||||||
|
* @param {Number} options.zoomIncrement - Amount to increment zoom factor by with every scroll after minScrolls
|
||||||
|
* @param {Boolean} options.enabled - Whether or not the scroll zoom logic is currently active
|
||||||
|
*/
|
||||||
|
$.SmartScrollZoom = function (options) {
|
||||||
|
//If this was not set to a viewer, throw an error
|
||||||
|
if (!options.viewer) {
|
||||||
|
throw new Error("SmartScrollZoom must be set to a viewer");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.viewer = options.viewer; //Set viewer
|
||||||
|
this.timeThreshold = options.timeThreshold || 400;
|
||||||
|
this.minScrolls = options.minScrolls || 2;
|
||||||
|
this.minZoomPerScroll = options.minZoomPerScroll || 1.2; //OpenSeadragon has a default of 1.2
|
||||||
|
this.maxZoomPerScroll = options.maxZoomPerScroll || 2.5;
|
||||||
|
this.zoomIncrement = options.zoomIncrement || 0.2;
|
||||||
|
this.enabled = options.enabled ? true : false;
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
//Create handler for logic
|
||||||
|
this.viewer.addHandler("canvas-scroll", function () {
|
||||||
|
//Do nothing if not enabled
|
||||||
|
if (!self.enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Create var to count number of consecutive scrolls that have taken place within the specified time limit of each other
|
||||||
|
if (typeof self.scrollNum == 'undefined') {
|
||||||
|
self.scrollNum = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Create var to store the time of the previous scroll that occurred
|
||||||
|
if (typeof self.lastScroll == 'undefined') {
|
||||||
|
self.lastScroll = new Date();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.currentScroll = new Date(); //Time that this scroll occurred at
|
||||||
|
|
||||||
|
//If the last scroll was less than 400 ms ago, increase the scroll count
|
||||||
|
if (self.currentScroll - self.lastScroll < self.timeThreshold) {
|
||||||
|
self.scrollNum++;
|
||||||
|
}
|
||||||
|
//Otherwise, reset the count and zoom speed
|
||||||
|
else {
|
||||||
|
self.scrollNum = 0;
|
||||||
|
self.viewer.zoomPerScroll = self.minZoomPerScroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
//If user has scrolled more than twice consecutively within 400 ms, increase the scroll speed with each consecutive scroll afterwards
|
||||||
|
if (self.scrollNum > self.minScrolls) {
|
||||||
|
//Limit maximum scroll speed to 2.5
|
||||||
|
if (self.viewer.zoomPerScroll <= self.maxZoomPerScroll) {
|
||||||
|
self.viewer.zoomPerScroll += self.zoomIncrement;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.lastScroll = self.currentScroll; //Set last scroll to now
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$.SmartScrollZoom.prototype = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set new options
|
||||||
|
*
|
||||||
|
* @function
|
||||||
|
* @memberof OpenSeadragon.SmartScrollZoom
|
||||||
|
* @since 1.0.0
|
||||||
|
* @version 1.0.0
|
||||||
|
* @param {Object} options
|
||||||
|
*/
|
||||||
|
setOptions: function (options) {
|
||||||
|
|
||||||
|
//If no new options were specifed, do nothing
|
||||||
|
if (!options) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Set time threshold
|
||||||
|
if (options.timeThreshold !== undefined) {
|
||||||
|
this.timeThreshold = options.timeThreshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Set minimum scroll number
|
||||||
|
if (options.minScrolls !== undefined) {
|
||||||
|
this.minScrolls = options.minScrolls;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Set minimum zoom per scroll
|
||||||
|
if (options.minZoomPerScroll !== undefined) {
|
||||||
|
this.minZoomPerScroll = options.minZoomPerScroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Set maximum zoom per scroll
|
||||||
|
if (options.maxZoomPerScroll !== undefined) {
|
||||||
|
this.maxZoomPerScroll = options.maxZoomPerScroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Set zoom increment
|
||||||
|
if (options.zoomIncrement !== undefined) {
|
||||||
|
this.zoomIncrement = options.zoomIncrement;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle the enabled option
|
||||||
|
*
|
||||||
|
* @function
|
||||||
|
* @memberof OpenSeadragon.SmartScrollZoom
|
||||||
|
* @since 1.0.0
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
toggleEnable: function () {
|
||||||
|
this.enabled = !this.enabled;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})(OpenSeadragon);
|
|
@ -0,0 +1,23 @@
|
||||||
|
const viewer = OpenSeadragon({
|
||||||
|
id: "viewer",
|
||||||
|
showNavigator: true,
|
||||||
|
showZoomControl: false,
|
||||||
|
showHomeControl: false,
|
||||||
|
showFullPageControl: false,
|
||||||
|
showRotationControl: false
|
||||||
|
});
|
||||||
|
|
||||||
|
viewer.smartScrollZoom({
|
||||||
|
minScrolls: 1,
|
||||||
|
timeThreshold: 0.01,
|
||||||
|
zoomIncrement: 1000
|
||||||
|
});
|
||||||
|
|
||||||
|
viewer.viewport.minZoomLevel = 0.033;
|
||||||
|
viewer.viewport.maxZoomLevel = 100;
|
||||||
|
viewer.viewport.defaultZoomLevel = 0.5;
|
||||||
|
viewer.viewport.scrollHandlerSpeed = 1000;
|
||||||
|
viewer.drawer.context.imageSmoothingEnabled = false;
|
||||||
|
viewer.gestureSettingsMouse.clickToZoom = false;
|
||||||
|
|
||||||
|
viewer.viewport.goHome(true);
|
|
@ -0,0 +1,181 @@
|
||||||
|
body {
|
||||||
|
background-color: #222;
|
||||||
|
margin: 0;
|
||||||
|
font-family: sans-serif;
|
||||||
|
color: white;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#viewer {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: #222;
|
||||||
|
background-image: url("image/su.png");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: auto;
|
||||||
|
background-position: center;
|
||||||
|
image-rendering: pixelated;
|
||||||
|
}
|
||||||
|
|
||||||
|
.r6o-editor {
|
||||||
|
z-index: 3;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.r6o-widget.comment {
|
||||||
|
font-size: 1.5em;
|
||||||
|
background-color: #333
|
||||||
|
}
|
||||||
|
|
||||||
|
.r6o-editor .r6o-arrow:after {
|
||||||
|
background-color: #333;
|
||||||
|
border: 3px white solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.r6o-editor .r6o-editor-inner .r6o-widget {
|
||||||
|
border: 3px white solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.r6o-editor .r6o-editor-inner {
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.r6o-footer {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
div {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#here-now {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
font-size: 1.5vh;
|
||||||
|
color: white;
|
||||||
|
padding: 15px;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#question {
|
||||||
|
font-size: 3vh;
|
||||||
|
width: 3vh;
|
||||||
|
height: 3vh;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
background-color: #333;
|
||||||
|
color: #fff;
|
||||||
|
border: 3px solid white;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 15px;
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#question img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#modal-overlay {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
#banner {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
#modal {
|
||||||
|
display: none;
|
||||||
|
font-size: 18pt;
|
||||||
|
position: fixed;
|
||||||
|
top: 50%;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease-in-out;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 1rem;
|
||||||
|
box-shadow: 0 0 10px 10px #000;
|
||||||
|
background-color: #333;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 4;
|
||||||
|
width: 80%;
|
||||||
|
height: 80%;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#modal-content {
|
||||||
|
position: relative;
|
||||||
|
padding: 33px;
|
||||||
|
text-align: center;
|
||||||
|
max-height: 100%;
|
||||||
|
font-size: 15pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes animateBg {
|
||||||
|
0% {
|
||||||
|
background-position: 0 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-position: 100% 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rainbow {
|
||||||
|
background: linear-gradient(90deg, #f1672b, #fea419, #efd515, #89d842, #35c2f9, #9b5fe0, #ff68cf, #f1672b, #fea419, #efd515, #89d842, #35c2f9, #9b5fe0, #ff68cf, #f1672b, #fea419, #efd515, #89d842, #35c2f9, #9b5fe0, #ff68cf, #f1672b);
|
||||||
|
background-size: 300% 100%;
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
animation: animateBg 5s infinite linear;
|
||||||
|
background-clip: text;
|
||||||
|
color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#close-button {
|
||||||
|
position: absolute;
|
||||||
|
font-size: 3rem;
|
||||||
|
top: 15px;
|
||||||
|
right: 30px;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 3vh;
|
||||||
|
height: 3vh;
|
||||||
|
background-color: #333;
|
||||||
|
color: #fff;
|
||||||
|
border: 3px solid white;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 15px;
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#open-button {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1081px) {
|
||||||
|
#viewer {
|
||||||
|
width: 100vw;
|
||||||
|
height: 66vh;
|
||||||
|
}
|
||||||
|
#modal-content {
|
||||||
|
font-size: 20pt;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
tornado
|
|
@ -0,0 +1,32 @@
|
||||||
|
import tornado.ioloop
|
||||||
|
import tornado.web
|
||||||
|
import tornado.websocket
|
||||||
|
|
||||||
|
class WebSocketHandler(tornado.websocket.WebSocketHandler):
|
||||||
|
clients = set()
|
||||||
|
|
||||||
|
def open(self):
|
||||||
|
WebSocketHandler.clients.add(self)
|
||||||
|
self.send_client_count()
|
||||||
|
|
||||||
|
def on_close(self):
|
||||||
|
WebSocketHandler.clients.remove(self)
|
||||||
|
self.send_client_count()
|
||||||
|
|
||||||
|
def send_client_count(self):
|
||||||
|
count = len(WebSocketHandler.clients)
|
||||||
|
for client in WebSocketHandler.clients:
|
||||||
|
try:
|
||||||
|
client.write_message(str(count))
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error sending message to client: {e}")
|
||||||
|
|
||||||
|
app = tornado.web.Application([
|
||||||
|
(r"/funsocket", WebSocketHandler),
|
||||||
|
])
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.listen(8080)
|
||||||
|
print("WebSocket server is running on port 8080")
|
||||||
|
tornado.ioloop.IOLoop.current().start()
|
||||||
|
|