Use an explicit APNG identifier for apng files with ImageMagick

Required as per Imagemgick docs: https://imagemagick.org/script/formats.php
This commit is contained in:
Kovid Goyal
2025-11-07 11:33:13 +05:30
parent 0195f0f5f6
commit f8c78909fa
2 changed files with 25 additions and 11 deletions

View File

@@ -12,9 +12,9 @@ import (
var _ = fmt.Print
func Render(path string, ro *images.RenderOptions, frames []images.IdentifyRecord) (ans []*image_frame, err error) {
func Render(path, original_file_path string, ro *images.RenderOptions, frames []images.IdentifyRecord) (ans []*image_frame, err error) {
ro.TempfilenameTemplate = shm_template
image_frames, filenames, err := images.RenderWithMagick(path, ro, frames)
image_frames, filenames, err := images.RenderWithMagick(path, original_file_path, ro, frames)
if err == nil {
ans = make([]*image_frame, len(image_frames))
for i, x := range image_frames {
@@ -41,7 +41,7 @@ func render_image_with_magick(imgd *image_data, src *opened_input) (err error) {
if err != nil {
return err
}
frames, err := images.IdentifyWithMagick(src.FileSystemName())
frames, err := images.IdentifyWithMagick(src.FileSystemName(), imgd.source_name)
if err != nil {
return err
}
@@ -56,7 +56,7 @@ func render_image_with_magick(imgd *image_data, src *opened_input) (err error) {
if scale_image(imgd) {
ro.ResizeTo.X, ro.ResizeTo.Y = imgd.canvas_width, imgd.canvas_height
}
imgd.frames, err = Render(src.FileSystemName(), &ro, frames)
imgd.frames, err = Render(src.FileSystemName(), imgd.source_name, &ro, frames)
if err != nil {
return err
}

View File

@@ -302,9 +302,13 @@ func check_resize(frame *ImageFrame, filename string) error {
}
expected_size := bytes_per_pixel * frame.Width * frame.Height
if sz < expected_size {
if bytes_per_pixel == 4 && sz == 3*frame.Width*frame.Height {
frame.Is_opaque = true
return nil
}
missing := expected_size - sz
if missing%(bytes_per_pixel*frame.Width) != 0 {
return fmt.Errorf("ImageMagick failed to resize correctly. It generated %d < %d of data (w=%d h=%d bpp=%d)", sz, expected_size, frame.Width, frame.Height, bytes_per_pixel)
return fmt.Errorf("ImageMagick failed to resize correctly. It generated %d < %d of data (w=%d h=%d bpp=%d frame-number: %d)", sz, expected_size, frame.Width, frame.Height, bytes_per_pixel, frame.Number)
}
frame.Height -= missing / (bytes_per_pixel * frame.Width)
}
@@ -439,11 +443,16 @@ func parse_identify_record(ans *IdentifyRecord, raw *IdentifyOutput) (err error)
return
}
func IdentifyWithMagick(path string) (ans []IdentifyRecord, err error) {
func IdentifyWithMagick(path, original_file_path string) (ans []IdentifyRecord, err error) {
cmd := []string{"identify"}
q := `{"fmt":"%m","canvas":"%g","transparency":"%A","gap":"%T","index":"%p","size":"%wx%h",` +
`"dpi":"%xx%y","dispose":"%D","orientation":"%[EXIF:Orientation]"},`
cmd = append(cmd, "-format", q, "--", path)
ext := filepath.Ext(original_file_path)
ipath := path
if strings.ToLower(ext) == ".apng" {
ipath = "APNG:" + path
}
cmd = append(cmd, "-format", q, "--", ipath)
output, err := RunMagick(path, cmd)
if err != nil {
return nil, fmt.Errorf("Failed to identify image at path: %s with error: %w", path, err)
@@ -476,10 +485,15 @@ type RenderOptions struct {
TempfilenameTemplate string
}
func RenderWithMagick(path string, ro *RenderOptions, frames []IdentifyRecord) (ans []*ImageFrame, fmap map[int]string, err error) {
func RenderWithMagick(path, original_file_path string, ro *RenderOptions, frames []IdentifyRecord) (ans []*ImageFrame, fmap map[int]string, err error) {
cmd := []string{"convert"}
ans = make([]*ImageFrame, 0, len(frames))
fmap = make(map[int]string, len(frames))
ext := filepath.Ext(original_file_path)
ipath := path
if strings.ToLower(ext) == ".apng" {
ipath = "APNG:" + path
}
defer func() {
if err != nil {
@@ -500,7 +514,7 @@ func RenderWithMagick(path string, ro *RenderOptions, frames []IdentifyRecord) (
if ro.Flop {
cmd = append(cmd, "-flop")
}
cpath := path
cpath := ipath
if ro.OnlyFirstFrame {
cpath += "[0]"
}
@@ -621,11 +635,11 @@ func RenderWithMagick(path string, ro *RenderOptions, frames []IdentifyRecord) (
}
func OpenImageFromPathWithMagick(path string) (ans *ImageData, err error) {
identify_records, err := IdentifyWithMagick(path)
identify_records, err := IdentifyWithMagick(path, path)
if err != nil {
return nil, fmt.Errorf("Failed to identify image at %#v with error: %w", path, err)
}
frames, filenames, err := RenderWithMagick(path, &RenderOptions{}, identify_records)
frames, filenames, err := RenderWithMagick(path, path, &RenderOptions{}, identify_records)
if err != nil {
return nil, fmt.Errorf("Failed to render image at %#v with error: %w", path, err)
}