Last active
January 18, 2025 19:21
-
-
Save muffinpowered/c45d1f4537879b504be38f4c6bb91cc5 to your computer and use it in GitHub Desktop.
golang photo2avif (libavif, cgo)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package main | |
| import ( | |
| "bytes" | |
| "image" | |
| _ "image/png" | |
| _ "image/jpeg" | |
| "fmt" | |
| "os" | |
| "unsafe" | |
| ) | |
| /* | |
| #cgo LDFLAGS: -lavif | |
| #include <avif/avif.h> | |
| #include <stdio.h> | |
| #include <string.h> | |
| uint8_t* encode( | |
| uint8_t *rgb_in, | |
| int width, int height, | |
| int depth, int chroma, | |
| int maxThreads, | |
| int quality, int speed, | |
| size_t *size | |
| ) { | |
| avifResult result; | |
| avifImage *image = avifImageCreate(width, height, depth, chroma); | |
| if (!image) { | |
| fprintf(stderr, "[avifImageCreate] Out of memory\n"); | |
| goto cleanup; | |
| } | |
| avifRGBImage rgb; | |
| avifRGBImageSetDefaults(&rgb, image); | |
| rgb.maxThreads = maxThreads; | |
| rgb.alphaPremultiplied = 1; | |
| result = avifRGBImageAllocatePixels(&rgb); | |
| if (result != AVIF_RESULT_OK) { | |
| fprintf(stderr, "[avifRGBImageAllocatePixels] Out of memory\n"); | |
| goto cleanup; | |
| } | |
| rgb.pixels = rgb_in; | |
| result = avifImageRGBToYUV(image, &rgb); | |
| if (result != AVIF_RESULT_OK) { | |
| fprintf(stderr, "[avifImageRGBToYUV] Out of memory\n"); | |
| goto cleanup; | |
| } | |
| avifRWData output = AVIF_DATA_EMPTY; | |
| avifEncoder *encoder = avifEncoderCreate(); | |
| if (!encoder) { | |
| fprintf(stderr, "[avifEncoderCreate] Out of memory\n"); | |
| goto cleanup; | |
| } | |
| encoder->maxThreads = maxThreads; | |
| encoder->quality = quality; | |
| encoder->speed = speed; | |
| result = avifEncoderAddImage(encoder, image, 1, AVIF_ADD_IMAGE_FLAG_SINGLE); | |
| if (result != AVIF_RESULT_OK) { | |
| fprintf(stderr, "Failed to add image to encoder: %s\n", avifResultToString(result)); | |
| goto cleanup; | |
| } | |
| result = avifEncoderFinish(encoder, &output); | |
| if (result != AVIF_RESULT_OK) { | |
| fprintf(stderr, "Failed to finish encode: %s\n", avifResultToString(result)); | |
| goto cleanup; | |
| } | |
| *size = output.size; | |
| return output.data; | |
| cleanup: | |
| if (image) { | |
| avifImageDestroy(image); | |
| } | |
| if (encoder) { | |
| avifEncoderDestroy(encoder); | |
| } | |
| avifRWDataFree(&output); | |
| avifRGBImageFreePixels(&rgb); | |
| } | |
| */ | |
| import "C" | |
| func Encode(img image.Image, depth, maxThreads, quality, speed int) []byte { | |
| bounds := img.Bounds() | |
| width := bounds.Dx() | |
| height := bounds.Dy() | |
| pixels := make([]byte, width*height*4) | |
| for y := 0; y < height; y++ { | |
| for x := 0; x < width; x++ { | |
| r, g, b, a := img.At(x, y).RGBA() | |
| i := (y*width + x) * 4 | |
| pixels[i+0] = byte(r >> 8) | |
| pixels[i+1] = byte(g >> 8) | |
| pixels[i+2] = byte(b >> 8) | |
| pixels[i+3] = byte(a >> 8) | |
| } | |
| } | |
| var size C.size_t | |
| data := C.encode((*C.uint8_t)(unsafe.Pointer(&pixels[0])), | |
| C.int(width), C.int(height), | |
| C.int(depth), C.int(C.AVIF_PIXEL_FORMAT_YUV444), | |
| C.int(maxThreads), | |
| C.int(quality), C.int(speed), | |
| &size) | |
| return C.GoBytes(unsafe.Pointer(data), C.int(size)) | |
| } | |
| func main() { | |
| DecodeNcodePhoto("./src/beads.png", "./dst/beads.avif") | |
| DecodeNcodePhoto("./src/icy-and-rocky-worlds.jpg", "./dst/icy-and-rocky-worlds.avif") | |
| fmt.Println("Files saved successfully!") | |
| } | |
| func DecodeNcodePhoto(srcPath, dstPath string) { | |
| src, err := os.ReadFile(srcPath) | |
| if err != nil { | |
| panic(err) | |
| } | |
| img, _, err := image.Decode(bytes.NewReader(src)) | |
| if err != nil { | |
| panic(err) | |
| } | |
| dst, err := os.Create(dstPath) | |
| if err != nil { | |
| panic(err) | |
| } | |
| defer dst.Close() | |
| encodedBytes := Encode(img, 8, 6, 75, 10) | |
| if _, err := dst.Write(encodedBytes); err != nil { | |
| panic(err) | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment