diff --git a/tools/cmd/icat/native.go b/tools/cmd/icat/native.go index 93909b518..489ce0ffe 100644 --- a/tools/cmd/icat/native.go +++ b/tools/cmd/icat/native.go @@ -13,7 +13,7 @@ import ( var _ = fmt.Print -func add_frame(imgd *image_data, img image.Image) { +func add_frame(imgd *image_data, img image.Image, is_opaque bool) { if flip { img = imaging.FlipV(img) } @@ -23,33 +23,48 @@ func add_frame(imgd *image_data, img image.Image) { b := img.Bounds() f := image_frame{width: b.Dx(), height: b.Dy()} dest_rect := image.Rect(0, 0, f.width, f.height) - var rgba *image.NRGBA - m, err := shm.CreateTemp("icat-*", uint64(f.width*f.height*4)) - if err != nil { - rgba = image.NewNRGBA(dest_rect) - } else { - rgba = &image.NRGBA{ - Pix: m.Slice(), - Stride: 4 * f.width, - Rect: dest_rect, + var final_img image.Image + + if is_opaque || remove_alpha != nil { + var rgb *images.NRGB + m, err := shm.CreateTemp("icat-*", uint64(f.width*f.height*3)) + if err != nil { + rgb = images.NewNRGB(dest_rect) + } else { + rgb = &images.NRGB{Pix: m.Slice(), Stride: 3 * f.width, Rect: dest_rect} + f.shm = m } - f.shm = m + imgd.format_uppercase = "RGB" + f.in_memory_bytes = rgb.Pix + final_img = rgb + } else { + var rgba *image.NRGBA + m, err := shm.CreateTemp("icat-*", uint64(f.width*f.height*4)) + if err != nil { + rgba = image.NewNRGBA(dest_rect) + } else { + rgba = &image.NRGBA{Pix: m.Slice(), Stride: 4 * f.width, Rect: dest_rect} + f.shm = m + } + imgd.format_uppercase = "RGBA" + f.in_memory_bytes = rgba.Pix + final_img = rgba } - images.PasteCenter(rgba, img, remove_alpha) - imgd.format_uppercase = "RGBA" - f.in_memory_bytes = rgba.Pix + images.PasteCenter(final_img, img, remove_alpha) imgd.frames = append(imgd.frames, &f) } -func load_one_frame_image(imgd *image_data, src *opened_input) (image.Image, error) { - img, err := imaging.Decode(src.file, imaging.AutoOrientation(true)) +func load_one_frame_image(imgd *image_data, src *opened_input) (img image.Image, is_opaque bool, err error) { + img, err = imaging.Decode(src.file, imaging.AutoOrientation(true)) src.Rewind() - if err == nil { - // reset the sizes as we read EXIF tags here which could have rotated the image - imgd.canvas_width = img.Bounds().Dx() - imgd.canvas_height = img.Bounds().Dy() - set_basic_metadata(imgd) + if err != nil { + return } + // reset the sizes as we read EXIF tags here which could have rotated the image + imgd.canvas_width = img.Bounds().Dx() + imgd.canvas_height = img.Bounds().Dy() + set_basic_metadata(imgd) + is_opaque = images.IsOpaque(img) if imgd.needs_scaling { if imgd.canvas_width < imgd.available_width && opts.ScaleUp && place != nil { r := float64(imgd.available_width) / float64(imgd.canvas_width) @@ -59,7 +74,7 @@ func load_one_frame_image(imgd *image_data, src *opened_input) (image.Image, err img = imaging.Resize(img, imgd.canvas_width, imgd.canvas_height, imaging.Lanczos) imgd.needs_scaling = false } - return img, err + return } func render_image_with_go(imgd *image_data, src *opened_input) (err error) { @@ -67,11 +82,11 @@ func render_image_with_go(imgd *image_data, src *opened_input) (err error) { case "GIF": return fmt.Errorf("TODO: implement GIF decoding") default: - img, err := load_one_frame_image(imgd, src) + img, is_opaque, err := load_one_frame_image(imgd, src) if err != nil { return err } - add_frame(imgd, img) + add_frame(imgd, img, is_opaque) } return nil } diff --git a/tools/utils/images/opaque.go b/tools/utils/images/opaque.go index c81c7db02..e1187fdd2 100644 --- a/tools/utils/images/opaque.go +++ b/tools/utils/images/opaque.go @@ -33,6 +33,8 @@ func IsOpaque(img image.Image) bool { return img.(*image.Paletted).Opaque() case *image.Uniform: return img.(*image.Uniform).Opaque() + case *image.YCbCr: + return img.(*image.YCbCr).Opaque() case *NRGB: return img.(*NRGB).Opaque() } diff --git a/tools/utils/images/to_rgb.go b/tools/utils/images/to_rgb.go index 3294abbfd..3a12eaf16 100644 --- a/tools/utils/images/to_rgb.go +++ b/tools/utils/images/to_rgb.go @@ -399,7 +399,7 @@ func (s *scanner_rgb) scan(x1, y1, x2, y2 int, dst []uint8) { } } -func paste_nrgb_onto_opaque(background *image.NRGBA, img image.Image, pos image.Point, bgcol *NRGBColor) { +func paste_nrgb_onto_opaque(background *NRGB, img image.Image, pos image.Point, bgcol *NRGBColor) { bg := NRGBColor{} if bgcol != nil { bg = *bgcol @@ -408,3 +408,11 @@ func paste_nrgb_onto_opaque(background *image.NRGBA, img image.Image, pos image. src := newScannerRGB(img, bg) run_paste(src, background, pos, func(dst []byte) {}) } + +func NewNRGB(r image.Rectangle) *NRGB { + return &NRGB{ + Pix: make([]uint8, 3*r.Dx()*r.Dy()), + Stride: 3 * r.Dx(), + Rect: r, + } +} diff --git a/tools/utils/images/to_rgba.go b/tools/utils/images/to_rgba.go index 82414056c..42f825772 100644 --- a/tools/utils/images/to_rgba.go +++ b/tools/utils/images/to_rgba.go @@ -330,7 +330,7 @@ type Scanner interface { bounds() image.Rectangle } -func run_paste(src Scanner, background *image.NRGBA, pos image.Point, postprocess func([]byte)) { +func run_paste(src Scanner, background image.Image, pos image.Point, postprocess func([]byte)) { pos = pos.Sub(background.Bounds().Min) pasteRect := image.Rectangle{Min: pos, Max: pos.Add(src.bounds().Size())} interRect := pasteRect.Intersect(background.Bounds()) @@ -338,15 +338,29 @@ func run_paste(src Scanner, background *image.NRGBA, pos image.Point, postproces return } bytes_per_pixel := src.bytes_per_pixel() + var stride int + var pix []uint8 + switch v := background.(type) { + case *image.NRGBA: + i := background.(*image.NRGBA) + stride = i.Stride + pix = i.Pix + case *NRGB: + i := background.(*NRGB) + stride = i.Stride + pix = i.Pix + default: + panic(fmt.Sprintf("Unsupported image type: %v", v)) + } parallel(interRect.Min.Y, interRect.Max.Y, func(ys <-chan int) { for y := range ys { x1 := interRect.Min.X - pasteRect.Min.X x2 := interRect.Max.X - pasteRect.Min.X y1 := y - pasteRect.Min.Y y2 := y1 + 1 - i1 := y*background.Stride + interRect.Min.X*bytes_per_pixel + i1 := y*stride + interRect.Min.X*bytes_per_pixel i2 := i1 + interRect.Dx()*bytes_per_pixel - dst := background.Pix[i1:i2] + dst := pix[i1:i2] src.scan(x1, y1, x2, y2, dst) postprocess(dst) } @@ -381,7 +395,7 @@ func Paste(background image.Image, img image.Image, pos image.Point, opaque_bg * case *image.NRGBA: paste_nrgba_onto_opaque(background.(*image.NRGBA), img, pos, opaque_bg) case *NRGB: - paste_nrgb_onto_opaque(background.(*image.NRGBA), img, pos, opaque_bg) + paste_nrgb_onto_opaque(background.(*NRGB), img, pos, opaque_bg) default: panic("Unsupported background image type") }