Created
November 6, 2023 21:53
-
-
Save mickael-kerjean/4e6aab46807fc30cf553bad4f67ba9d6 to your computer and use it in GitHub Desktop.
This file contains 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
#include <string.h> | |
#include <png.h> | |
#include <stdlib.h> | |
#include "webp/encode.h" | |
#include "utils.h" | |
static int filestash_png_writer(const uint8_t* data, size_t data_size, const WebPPicture* const pic) { | |
FILE* const out = (FILE*)pic->custom_ptr; | |
return data_size ? (fwrite(data, data_size, 1, out) == 1) : 1; | |
} | |
int png_to_webp(int inputDesc, int outputDesc, int targetSize) { | |
#ifdef HAS_DEBUG | |
clock_t t; | |
t = clock(); | |
#endif | |
WebPPicture picture; | |
FILE* input = fdopen(inputDesc, "r"); | |
FILE* output = fdopen(outputDesc, "w"); | |
png_image image; | |
memset(&image, 0, sizeof image); | |
image.version = PNG_IMAGE_VERSION; | |
DEBUG("reading png"); | |
if (!png_image_begin_read_from_stdio(&image, input)) { | |
ERROR("png_image_begin_read_from_stdio"); | |
return 1; | |
} | |
DEBUG("allocate"); | |
png_bytep buffer; | |
image.format = PNG_FORMAT_RGBA; | |
buffer = malloc(PNG_IMAGE_SIZE(image)); | |
if (buffer == NULL) { | |
ERROR("png_malloc"); | |
png_image_free(&image); | |
return 1; | |
} | |
DEBUG("start reading"); | |
if (!png_image_finish_read(&image, NULL, buffer, 0, NULL)) { | |
ERROR("png_image_finish_read"); | |
png_image_free(&image); | |
free(buffer); | |
return 1; | |
} | |
///////////////////////////////////////////// | |
// encode to webp | |
/* | |
DEBUG("start encoding"); | |
if (!WebPPictureInit(&picture)) { | |
ERROR("WebPPictureInit"); | |
png_image_free(&image); | |
free(buffer); | |
return 1; | |
} | |
picture.width = image.width; | |
picture.height = image.height; | |
picture.writer = filestash_png_writer; | |
picture.custom_ptr = output; | |
if(!WebPPictureAlloc(&picture)) { | |
ERROR("WebPPictureAlloc"); | |
png_image_free(&image); | |
free(buffer); | |
return 1; | |
} | |
DEBUG("start encoding import"); | |
if(!WebPPictureImportRGBX(&picture, buffer, PNG_IMAGE_ROW_STRIDE(image))) { | |
ERROR("WebPPictureImport"); | |
png_image_free(&image); | |
free(buffer); | |
return 1; | |
} | |
// png_image_free(&image); | |
// free(buffer); | |
WebPConfig webp_config_output; | |
DEBUG("start encoding config init"); | |
if (!WebPConfigInit(&webp_config_output)) { | |
ERROR("ERR config init"); | |
WebPPictureFree(&picture); | |
return 1; | |
} | |
WebPConfigLosslessPreset(&webp_config_output, 6); // TODO: 0 is faster | |
webp_config_output.method = 0; | |
webp_config_output.quality = 30; | |
if (!WebPValidateConfig(&webp_config_output)) { | |
ERROR("ERR WEBP VALIDATION"); | |
WebPPictureFree(&picture); | |
return 1; | |
} | |
/* | |
DEBUG("rescale start"); | |
if (image.width > targetSize && image.height > targetSize) { | |
float ratioHeight = (float) image.height / targetSize; | |
float ratioWidth = (float) image.width / targetSize; | |
float ratio = ratioWidth > ratioHeight ? ratioHeight : ratioWidth; | |
if (!WebPPictureRescale(&picture, image.width / ratio, image.height / ratio)) { | |
DEBUG("ERR Rescale"); | |
WebPPictureFree(&picture); | |
return 1; | |
} | |
} | |
*//* | |
DEBUG("encoder start"); | |
if (!WebPEncode(&webp_config_output, &picture)) { | |
ERROR("ERR WEBP ENCODE"); | |
WebPPictureFree(&picture); | |
return 1; | |
} | |
DEBUG("encoder done"); | |
WebPPictureFree(&picture); | |
DEBUG("cleaning up"); | |
*/ | |
DEBUG("> write"); | |
if (!png_image_write_to_stdio(&image, output, 0, buffer, 0, NULL)) { | |
DEBUG("png_image_write_to_stdio"); | |
png_image_free(&image); | |
free(buffer); | |
return 1; | |
} | |
return 0; | |
} | |
int png_to_png(int inputDesc, int outputDesc, int targetSize) { | |
#ifdef HAS_DEBUG | |
clock_t t; | |
t = clock(); | |
#endif | |
WebPPicture picture; | |
FILE* input = fdopen(inputDesc, "r"); | |
FILE* output = fdopen(outputDesc, "w"); | |
png_image image; | |
memset(&image, 0, sizeof image); | |
image.version = PNG_IMAGE_VERSION; | |
DEBUG("> reading png"); | |
if (!png_image_begin_read_from_stdio(&image, input)) { | |
DEBUG("png_image_begin_read_from_stdio"); | |
return 1; | |
} | |
DEBUG("> allocate"); | |
png_bytep buffer; | |
image.format = PNG_FORMAT_RGBA; | |
buffer = malloc(PNG_IMAGE_SIZE(image)); | |
if (buffer == NULL) { | |
DEBUG("png_malloc"); | |
png_image_free(&image); | |
return 1; | |
} | |
DEBUG("> start reading"); | |
if (!png_image_finish_read(&image, NULL, buffer, 0, NULL)) { | |
DEBUG("png_image_finish_read"); | |
png_image_free(&image); | |
free(buffer); | |
return 1; | |
} | |
DEBUG("> write"); | |
if (!png_image_write_to_stdio(&image, output, 0, buffer, 0, NULL)) { | |
DEBUG("png_image_write_to_stdio"); | |
png_image_free(&image); | |
free(buffer); | |
return 1; | |
} | |
DEBUG("> end"); | |
png_image_free(&image); | |
free(buffer); | |
return 0; | |
} |
This file contains 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 plg_image_c | |
// #include "image_png.h" | |
// #cgo LDFLAGS: -l:libpng.a -l:libz.a -l:libwebp.a -lpthread -lm | |
import "C" | |
func png(input uintptr, output uintptr, size int) { | |
C.png_to_png(C.int(input), C.int(output), 200) | |
return | |
} |
This file contains 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
#include <stdio.h> | |
#include <stdlib.h> | |
int png_to_png(int input, int output, int targetSize); | |
int png_to_webp(int input, int output, int targetSize); |
This file contains 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 plg_image_c | |
import ( | |
. "github.com/mickael-kerjean/filestash/server/common" | |
"io" | |
"net/http" | |
"os" | |
) | |
func init() { | |
Hooks.Register.Thumbnailer("image/jpeg", thumbnailer{ | |
runner(jpeg), | |
"image/jpeg", | |
}) | |
Hooks.Register.Thumbnailer("image/png", thumbnailer{ | |
runner(png), | |
"image/png", | |
}) | |
} | |
type thumbnailer struct { | |
fn func(input io.ReadCloser) (io.ReadCloser, error) | |
mime string | |
} | |
func (this thumbnailer) Generate(reader io.ReadCloser, ctx *App, res *http.ResponseWriter, req *http.Request) (io.ReadCloser, error) { | |
thumb, err := this.fn(reader) | |
if err == nil && this.mime != "" { | |
(*res).Header().Set("Content-Type", this.mime) | |
} | |
return thumb, err | |
} | |
/* | |
* uuuh, what is this stuff you might rightly wonder? Trying to send a go stream to C isn't obvious, | |
* but if you try to stream from C back to go in the same time, this is what you endup with. | |
* To my knowledge using file descriptor is the best way we can do that if we don't make the assumption | |
* that everything fits in memory. | |
*/ | |
func runner(fn func(uintptr, uintptr, int)) func(io.ReadCloser) (io.ReadCloser, error) { | |
return func(inputGo io.ReadCloser) (io.ReadCloser, error) { | |
inputC, tmpw, err := os.Pipe() | |
if err != nil { | |
return nil, err | |
} | |
outputGo, outputC, err := os.Pipe() | |
if err != nil { | |
tmpw.Close() | |
return nil, err | |
} | |
go func() { | |
if _, err = io.Copy(tmpw, inputGo); err != nil { | |
Log.Error("plg_image_c::copy %s", err.Error()) | |
} | |
}() | |
go func() { | |
fn(inputC.Fd(), outputC.Fd(), 200) // <-- all this code so we can do that | |
tmpw.Close() | |
outputC.Close() | |
inputC.Close() | |
}() | |
return outputGo, nil | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment