-
-
Notifications
You must be signed in to change notification settings - Fork 19
Add gif support #249
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Add gif support #249
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| When the gif library is /gifLib/ { | ||
| set gifCache [dict create] | ||
|
|
||
| When /someone/ wishes /p/ displays gif /path/ with /...options/ &\ | ||
| /p/ has resolved geometry /geom/ { | ||
|
|
||
| if {![dict exists $gifCache $path]} { | ||
| set path_resolved $path | ||
|
|
||
| # Support remote images via HTTP | ||
| if {[string match "http*://*" $path]} { | ||
| set path_resolved /tmp/[regsub -all {\W+} $path "_"].gif | ||
| if {![file exists $path_resolved]} { | ||
| puts stderr "gif: downloading $path to $path_resolved" | ||
osnr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if {[catch {exec curl -s -L -o $path_resolved $path} err]} { | ||
| puts stderr "gif: download failed: $err" | ||
| return | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if {![file exists $path_resolved]} { | ||
| set path_resolved_local "$::env(HOME)/folk-images/$path_resolved" | ||
| if {[file exists $path_resolved_local]} { | ||
| set path_resolved $path_resolved_local | ||
| } | ||
| } | ||
| if {![file exists $path_resolved]} { | ||
| # Fallback to current directory | ||
| set path_resolved_local [file join [pwd] $path_resolved] | ||
| if {[file exists $path_resolved_local]} { | ||
| set path_resolved $path_resolved_local | ||
| } | ||
| } | ||
|
|
||
| if {![file exists $path_resolved]} { | ||
| return | ||
osnr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| try { | ||
| dict set gifCache $path [$gifLib loadGif $path_resolved] | ||
| } on error {err} { | ||
| puts stderr "gif: error loading $path: $err" | ||
| return | ||
| } | ||
| } | ||
|
|
||
| set gif [dict get $gifCache $path] | ||
| set frames [dict get $gif frames] | ||
| set delays [dict get $gif delays] | ||
|
|
||
| set totalDuration 0 | ||
| set cumulativeDelays [list] | ||
| foreach d $delays { | ||
| if {$d <= 10} { set d 100 } | ||
| incr totalDuration $d | ||
| lappend cumulativeDelays $totalDuration | ||
| } | ||
|
|
||
| if {[llength $frames] == 0} { return } | ||
|
|
||
| if {[llength $frames] == 1} { | ||
| Wish $p displays image [lindex $frames 0] with {*}$options | ||
| return | ||
| } | ||
|
|
||
| When the clock time is /t/ { | ||
| set ms [expr {int($t * 1000) % $totalDuration}] | ||
| set frameIdx 0 | ||
| foreach cd $cumulativeDelays { | ||
| if {$ms < $cd} { break } | ||
| incr frameIdx | ||
| } | ||
| if {$frameIdx >= [llength $frames]} { set frameIdx 0 } | ||
| set im [lindex $frames $frameIdx] | ||
|
|
||
| Wish $p displays image $im with {*}$options | ||
| } | ||
| } | ||
|
|
||
| When /someone/ wishes /p/ displays gif /path/ { | ||
| Wish $p displays gif $path with scale 1.0 | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| When the image library is /imageLib/ { | ||
| set cc [C] | ||
| $cc extend $imageLib | ||
| $cc cflags -I[pwd]/vendor/gifdec | ||
| $cc endcflags [pwd]/vendor/gifdec/gifdec.c | ||
| $cc include "gifdec.h" | ||
| $cc include <stdlib.h> | ||
|
|
||
| $cc proc loadGif {char* filename} Jim_Obj* { | ||
| gd_GIF *gif = gd_open_gif(filename); | ||
| if (!gif) { | ||
| FOLK_ERROR("loadGif: Failed to open %s", filename); | ||
| } | ||
|
|
||
| Jim_Obj *framesList = Jim_NewListObj(interp, NULL, 0); | ||
| Jim_Obj *delaysList = Jim_NewListObj(interp, NULL, 0); | ||
|
|
||
| while (gd_get_frame(gif)) { | ||
| Image im; | ||
| im.width = gif->width; | ||
| im.height = gif->height; | ||
| im.components = 3; | ||
| im.bytesPerRow = im.width * 3; | ||
| im.data = malloc(im.bytesPerRow * im.height); | ||
| im.uniq = 0; | ||
|
|
||
| gd_render_frame(gif, im.data); | ||
|
|
||
| // Box the Image struct into a Tcl object | ||
| Jim_Obj *imObj; | ||
| $[$cc ret Image imObj im] | ||
| Jim_ListAppendElement(interp, framesList, imObj); | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am sort of uncomfortable with how little I understand this (especially line 31). It works but I'd like to understand the mechanisms behind
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a pretty deep cut -- I think I originally made |
||
|
|
||
| // gifdec delay is in 1/100s of a second. Convert to milliseconds. | ||
| Jim_ListAppendElement(interp, delaysList, Jim_NewIntObj(interp, gif->gce.delay * 10)); | ||
| } | ||
|
|
||
| uint16_t w = gif->width; | ||
| uint16_t h = gif->height; | ||
| gd_close_gif(gif); | ||
|
|
||
| Jim_Obj *result = Jim_NewDictObj(interp, NULL, 0); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be more elegant to define this as a struct Gif and return that on the stack instead of having the Tcl encoding inline here, I think |
||
| Jim_DictAddElement(interp, result, Jim_NewStringObj(interp, "frames", -1), framesList); | ||
| Jim_DictAddElement(interp, result, Jim_NewStringObj(interp, "delays", -1), delaysList); | ||
| Jim_DictAddElement(interp, result, Jim_NewStringObj(interp, "width", -1), Jim_NewIntObj(interp, w)); | ||
| Jim_DictAddElement(interp, result, Jim_NewStringObj(interp, "height", -1), Jim_NewIntObj(interp, h)); | ||
| return result; | ||
| } | ||
|
|
||
| set gifLib [$cc compile] | ||
| Claim the gif library is $gifLib | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure how much I like adding yet another dependency to image.folk. There isn't a good story for graceful degradation here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could split it into independent handlers for gif/png/jpeg that all check if the path applies to them and return if not?