diff --git a/Makefile b/Makefile index df8b1862..2c7ce35e 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,17 @@ all: - make build_backend + make build_init + make build_frontend + GOARCH=amd64 GOOS=linux make build_backend + # GOARCH=arm GOARM=7 GOOS=linux make build_backend build_init: - find server/plugin/plg_* -type f -name "install.sh" -exec {} \; go generate -x ./server/... build_frontend: NODE_ENV=production npm run build build_backend: - PKG_CONFIG_PATH=/usr/local/lib/pkgconfig/ CGO_CFLAGS_ALLOW='-fopenmp' go build -mod=vendor --tags "fts5" -ldflags "-X github.com/mickael-kerjean/filestash/server/common.BUILD_DATE=`date -u +%Y%m%d` -X github.com/mickael-kerjean/filestash/server/common.BUILD_REF=`git rev-parse HEAD`" -o dist/filestash server/main.go + CGO_ENABLED=0 go build -ldflags="-extldflags=-static" -mod=vendor --tags "fts5" -o dist/filestash server/main.go + +clean_frontend: + rm -rf server/ctrl/static/www/ diff --git a/config/mime.json b/config/mime.json index 2b8e04d9..192dbe1d 100644 --- a/config/mime.json +++ b/config/mime.json @@ -22,6 +22,7 @@ "crw": "image/x-canon-crw", "css": "text/css", "csv": "text/csv", + "dcm": "image/dicom", "dcr": "image/x-kodak-dcr", "deb": "application/octet-stream", "der": "application/x-x509-ca-cert", diff --git a/server/plugin/plg_image_thumbnail/Makefile b/server/plugin/plg_image_thumbnail/Makefile index b5f6b8bc..9e6ee4b7 100644 --- a/server/plugin/plg_image_thumbnail/Makefile +++ b/server/plugin/plg_image_thumbnail/Makefile @@ -1,9 +1,38 @@ +PLATFORM=linux +ARCH=amd64 + all: @make jpeg @make png + @make raw -jpeg: - gcc -Wall src/jpeg_to_jpeg.c -o dist/jpeg.bin -l:libjpeg.a +image_jpeg.o: + gcc -Wall -c src/image_jpeg.c -o src/image_jpeg.o -png: - gcc -Wall src/png_to_webp.c -o dist/png.bin -l:libpng.a -l:libz.a -l:libwebp.a -lpthread -lm +image_png.o: + gcc -Wall -c src/image_png.c -o src/image_png.o + +image_raw.o: + gcc -Wall -c src/image_raw.c -o src/image_raw.o + +jpeg: image_jpeg.o + gcc -static src/main_jpeg_to_jpeg.c src/image_jpeg.o -o dist/jpeg_$(PLATFORM)_$(ARCH).bin -l:libjpeg.a + sha256sum ./dist/jpeg_$(PLATFORM)_$(ARCH).bin | awk '{ printf $$1 }' > dist/jpeg_$(PLATFORM)_$(ARCH).bin.sha256 + +png: image_png.o + gcc -static -Wall src/main_png_to_webp.c src/image_png.o -o dist/png_$(PLATFORM)_$(ARCH).bin -l:libpng.a -l:libz.a -l:libwebp.a -lpthread -lm + sha256sum ./dist/png_$(PLATFORM)_$(ARCH).bin | awk '{ printf $$1 }' > dist/png_$(PLATFORM)_$(ARCH).bin.sha256 + +raw: image_raw.o image_jpeg.o + gcc -Wall -c src/main_raw_to_jpeg.c -o src/main_raw_to_jpeg.o + # libraw was configured like this: ./configure --disable-openmp --disable-lcms + g++ -static src/main_raw_to_jpeg.o src/image_raw.o src/image_jpeg.o -o dist/raw_$(PLATFORM)_$(ARCH).bin -l:libraw.a -l:libjpeg.a -l:libz.a -l:libstdc++.a -lm + sha256sum ./dist/raw_$(PLATFORM)_$(ARCH).bin | awk '{ printf $$1 }' > dist/raw_$(PLATFORM)_$(ARCH).bin.sha256 + +clean: + rm src/*.o dist/*_$(PLATFORM)_$(ARCH).bin* || true + +test: + @gcc -Wall src/test.c -o test.bin -ljpeg -lpng -lz -lwebp -lpthread -lm -lraw + @./test.bin /home/mickael/Downloads/ + @rm test.bin diff --git a/server/plugin/plg_image_thumbnail/dist/jpeg_linux_amd64.bin b/server/plugin/plg_image_thumbnail/dist/jpeg_linux_amd64.bin index 1083f2d7..175a7e26 100755 Binary files a/server/plugin/plg_image_thumbnail/dist/jpeg_linux_amd64.bin and b/server/plugin/plg_image_thumbnail/dist/jpeg_linux_amd64.bin differ diff --git a/server/plugin/plg_image_thumbnail/dist/jpeg_linux_arm.bin b/server/plugin/plg_image_thumbnail/dist/jpeg_linux_arm.bin index f7acb733..c5f640ef 100755 Binary files a/server/plugin/plg_image_thumbnail/dist/jpeg_linux_arm.bin and b/server/plugin/plg_image_thumbnail/dist/jpeg_linux_arm.bin differ diff --git a/server/plugin/plg_image_thumbnail/dist/png_linux_amd64.bin b/server/plugin/plg_image_thumbnail/dist/png_linux_amd64.bin index 4d8a4601..bd451ce0 100755 Binary files a/server/plugin/plg_image_thumbnail/dist/png_linux_amd64.bin and b/server/plugin/plg_image_thumbnail/dist/png_linux_amd64.bin differ diff --git a/server/plugin/plg_image_thumbnail/dist/png_linux_arm.bin b/server/plugin/plg_image_thumbnail/dist/png_linux_arm.bin index 0d7d9341..72f9b4ca 100755 Binary files a/server/plugin/plg_image_thumbnail/dist/png_linux_arm.bin and b/server/plugin/plg_image_thumbnail/dist/png_linux_arm.bin differ diff --git a/server/plugin/plg_image_thumbnail/index.go b/server/plugin/plg_image_thumbnail/index.go index 1e527ac6..8865b622 100644 --- a/server/plugin/plg_image_thumbnail/index.go +++ b/server/plugin/plg_image_thumbnail/index.go @@ -9,6 +9,9 @@ import ( "io" "net/http" "os" + "os/exec" + "sync" + "time" ) //go:embed dist/placeholder.png @@ -17,6 +20,16 @@ var placeholder []byte func init() { Hooks.Register.Thumbnailer("image/png", thumbnailBuilder{thumbnailPng}) Hooks.Register.Thumbnailer("image/jpeg", thumbnailBuilder{thumbnailJpeg}) + for _, mType := range []string{ + "image/x-canon-cr2", "image/x-fuji-raf", "image/x-nikon-nef", + "image/x-nikon-nrw", "image/x-epson-erf", + // "image/tiff", + // "image/x-kodak-dcr", "image/x-hasselblad-3fr", + // "image/x-raw", + } { + Hooks.Register.Thumbnailer(mType, thumbnailBuilder{thumbnailRaw}) + } + // TODO: Hooks.Register.ProcessFileContentBeforeSend for raw files rendering } func thumbnailPng(reader io.ReadCloser, ctx *App, res *http.ResponseWriter, req *http.Request) (io.ReadCloser, error) { @@ -35,6 +48,33 @@ func thumbnailPng(reader io.ReadCloser, ctx *App, res *http.ResponseWriter, req func thumbnailJpeg(reader io.ReadCloser, ctx *App, res *http.ResponseWriter, req *http.Request) (io.ReadCloser, error) { h := (*res).Header() r, err := createThumbnailForJpeg(reader) + if err != nil { + h.Set("Content-Type", "image/png") + h.Set("Cache-Control", "max-age=1") + return NewReadCloserFromBytes(placeholder), nil + } + h.Set("Content-Type", "image/jpeg") + h.Set("Cache-Control", fmt.Sprintf("max-age=%d", 3600*12)) + return r, nil +} + +func thumbnailRaw(reader io.ReadCloser, ctx *App, res *http.ResponseWriter, req *http.Request) (io.ReadCloser, error) { + h := (*res).Header() + r, err := createThumbnailForRaw(reader) + if err != nil { + h.Set("Content-Type", "image/png") + h.Set("Cache-Control", "max-age=1") + return NewReadCloserFromBytes(placeholder), nil + } + h.Set("Content-Type", "image/jpeg") + h.Set("Cache-Control", fmt.Sprintf("max-age=%d", 3600*12)) + return r, nil +} + +func renderRaw(reader io.ReadCloser, ctx *App, res *http.ResponseWriter, req *http.Request) (io.ReadCloser, error) { + h := (*res).Header() + r, err := createThumbnailForRaw(reader) + // r, err := createExtractForRaw(reader) if err != nil { h.Set("Content-Type", "image/png") return NewReadCloserFromBytes(placeholder), nil @@ -52,34 +92,67 @@ func (this thumbnailBuilder) Generate(reader io.ReadCloser, ctx *App, res *http. return this.fn(reader, ctx, res, req) } -func setupProgram(name string, raw []byte) error { - p := "/tmp/" + name +type ThumbnailExecutable struct { + Name string + Binary *[]byte + Checksum []byte + isValid bool + lastVerify time.Time + sync.Mutex +} + +func (this ThumbnailExecutable) Init() { + p := "/tmp/" + this.Name f, err := os.OpenFile(p, os.O_RDONLY, os.ModePerm) if err != nil { outFile, err := os.OpenFile(p, os.O_CREATE|os.O_WRONLY, os.ModePerm) if err != nil { - return err + Log.Warning("plg_image_thumbnail::init::run::openFile '%s'", this.Name) + return } - outFile.Write(raw) + outFile.Write(*this.Binary) if err = outFile.Close(); err != nil { - return err - } - f, err = os.OpenFile(p, os.O_RDONLY, os.ModePerm) - if err != nil { - return err + Log.Warning("plg_image_thumbnail::init::run::close '%s'", this.Name) + return } } - b := make([]byte, 5) - n, err := f.Read(b) - if err != nil { - f.Close() - return err - } else if n != 5 { - f.Close() - return errors.New("unexpected read") - } else if bytes.Equal(b, raw[:5]) == false { - f.Close() - return errors.New("different data") + if f.Close() != nil { + Log.Warning("plg_image_thumbnail::init::close '%s'", this.Name) } - return f.Close() +} + +func (this *ThumbnailExecutable) verify() bool { + this.Lock() + defer this.Unlock() + if time.Since(this.lastVerify) > 30*time.Second { + this.lastVerify = time.Now() + f, err := os.OpenFile("/tmp/"+this.Name, os.O_RDONLY, os.ModePerm) + if err == nil && bytes.Equal([]byte(HashStream(f, 0)), this.Checksum) { + this.isValid = true + } + } + return this.isValid +} + +func (this *ThumbnailExecutable) Execute(reader io.ReadCloser) (io.ReadCloser, error) { + if this.verify() == false { + Log.Error("plg_image_thumbnail::execution abort after verification on '%s'", this.Name) + reader.Close() + return nil, ErrFilesystemError + } + // TODO: rate limit this + var buf bytes.Buffer + var errBuff bytes.Buffer + cmd := exec.Command("/tmp/" + this.Name) + cmd.Stdin = reader + cmd.Stdout = &buf + cmd.Stderr = &errBuff + if err := cmd.Run(); err != nil { + reader.Close() + Log.Debug("plg_image_thumbmail::resize %s ERR %s", this.Name, string(errBuff.Bytes())) + return nil, errors.New(string(errBuff.Bytes())) + } + cmd.Wait() + reader.Close() + return NewReadCloserFromBytes(buf.Bytes()), nil } diff --git a/server/plugin/plg_image_thumbnail/resize_jpeg.go b/server/plugin/plg_image_thumbnail/resize_jpeg.go index f1a3bff7..d9a183f1 100644 --- a/server/plugin/plg_image_thumbnail/resize_jpeg.go +++ b/server/plugin/plg_image_thumbnail/resize_jpeg.go @@ -1,34 +1,19 @@ package plg_image_thumbnail import ( - "bytes" - "errors" - . "github.com/mickael-kerjean/filestash/server/common" "io" - "os/exec" ) -func createThumbnailForJpeg(reader io.ReadCloser) (io.ReadCloser, error) { - name := "thumbnail_web.bin" - err := setupProgram(name, binaryThumbnailJpeg) - if err != nil { - Log.Warning("plg_image_thumbnail %s") - reader.Close() - return nil, err - } - - var buf bytes.Buffer - var errBuff bytes.Buffer - cmd := exec.Command("/tmp/" + name) - cmd.Stdin = reader - cmd.Stdout = &buf - cmd.Stderr = &errBuff - if err := cmd.Run(); err != nil { - reader.Close() - Log.Debug("plg_image_thumbmail::resize_jpeg ERR %s", string(errBuff.Bytes())) - return nil, errors.New(string(errBuff.Bytes())) - } - cmd.Wait() - reader.Close() - return NewReadCloserFromBytes(buf.Bytes()), nil +var exeForJpeg ThumbnailExecutable = ThumbnailExecutable{ + Name: "thumbnail_jpeg.bin", + Binary: &binaryThumbnailJpeg, + Checksum: checksumJpeg, +} + +func init() { + exeForJpeg.Init() +} + +func createThumbnailForJpeg(reader io.ReadCloser) (io.ReadCloser, error) { + return exeForJpeg.Execute(reader) } diff --git a/server/plugin/plg_image_thumbnail/resize_jpeg_linux_amd64.go b/server/plugin/plg_image_thumbnail/resize_jpeg_linux_amd64.go index 45b05a6e..c718b346 100644 --- a/server/plugin/plg_image_thumbnail/resize_jpeg_linux_amd64.go +++ b/server/plugin/plg_image_thumbnail/resize_jpeg_linux_amd64.go @@ -6,3 +6,6 @@ import ( //go:embed dist/jpeg_linux_amd64.bin var binaryThumbnailJpeg []byte + +//go:embed dist/jpeg_linux_amd64.bin.sha256 +var checksumJpeg []byte diff --git a/server/plugin/plg_image_thumbnail/resize_jpeg_linux_arm.go b/server/plugin/plg_image_thumbnail/resize_jpeg_linux_arm.go index 2b968491..20beece3 100644 --- a/server/plugin/plg_image_thumbnail/resize_jpeg_linux_arm.go +++ b/server/plugin/plg_image_thumbnail/resize_jpeg_linux_arm.go @@ -6,3 +6,6 @@ import ( //go:embed dist/jpeg_linux_arm.bin var binaryThumbnailJpeg []byte + +//go:embed dist/jpeg_linux_arm.bin.sha256 +var checksumJpeg []byte diff --git a/server/plugin/plg_image_thumbnail/resize_png.go b/server/plugin/plg_image_thumbnail/resize_png.go index 2a2ee88a..067b24ac 100644 --- a/server/plugin/plg_image_thumbnail/resize_png.go +++ b/server/plugin/plg_image_thumbnail/resize_png.go @@ -1,35 +1,19 @@ package plg_image_thumbnail import ( - "bytes" - _ "embed" - "errors" - . "github.com/mickael-kerjean/filestash/server/common" "io" - "os/exec" ) -func createThumbnailForPng(reader io.ReadCloser) (io.ReadCloser, error) { - name := "thumbnail_webp.bin" - err := setupProgram(name, binaryThumbnailPng) - if err != nil { - Log.Warning("plg_image_thumbnail %s") - reader.Close() - return nil, err - } - - var buf bytes.Buffer - var errBuff bytes.Buffer - cmd := exec.Command("/tmp/" + name) - cmd.Stdin = reader - cmd.Stdout = &buf - cmd.Stderr = &errBuff - if err := cmd.Run(); err != nil { - reader.Close() - Log.Debug("plg_image_thumbmail::resize_webp ERR %s", string(errBuff.Bytes())) - return nil, errors.New(string(errBuff.Bytes())) - } - cmd.Wait() - reader.Close() - return NewReadCloserFromBytes(buf.Bytes()), nil +var exeForPng ThumbnailExecutable = ThumbnailExecutable{ + Name: "thumbnail_png.bin", + Binary: &binaryThumbnailPng, + Checksum: checksumPng, +} + +func init() { + exeForPng.Init() +} + +func createThumbnailForPng(reader io.ReadCloser) (io.ReadCloser, error) { + return exeForPng.Execute(reader) } diff --git a/server/plugin/plg_image_thumbnail/resize_png_linux_amd64.go b/server/plugin/plg_image_thumbnail/resize_png_linux_amd64.go index faf3156b..c6e2f698 100644 --- a/server/plugin/plg_image_thumbnail/resize_png_linux_amd64.go +++ b/server/plugin/plg_image_thumbnail/resize_png_linux_amd64.go @@ -6,3 +6,6 @@ import ( //go:embed dist/png_linux_amd64.bin var binaryThumbnailPng []byte + +//go:embed dist/png_linux_amd64.bin.sha256 +var checksumPng []byte diff --git a/server/plugin/plg_image_thumbnail/resize_png_linux_arm.go b/server/plugin/plg_image_thumbnail/resize_png_linux_arm.go index b2163e69..f5dbef5a 100644 --- a/server/plugin/plg_image_thumbnail/resize_png_linux_arm.go +++ b/server/plugin/plg_image_thumbnail/resize_png_linux_arm.go @@ -6,3 +6,6 @@ import ( //go:embed dist/png_linux_arm.bin var binaryThumbnailPng []byte + +//go:embed dist/png_linux_arm.bin.sha256 +var checksumPng []byte diff --git a/server/plugin/plg_image_thumbnail/resize_raw.go b/server/plugin/plg_image_thumbnail/resize_raw.go new file mode 100644 index 00000000..c501e24f --- /dev/null +++ b/server/plugin/plg_image_thumbnail/resize_raw.go @@ -0,0 +1,50 @@ +package plg_image_thumbnail + +import ( + "io" +) + +var exeForRaw ThumbnailExecutable = ThumbnailExecutable{ + Name: "thumbnail_raw.bin", + Binary: &binaryThumbnailRaw, + Checksum: checksumRaw, +} + +func init() { + exeForRaw.Init() +} + +func createThumbnailForRaw(reader io.ReadCloser) (io.ReadCloser, error) { + return exeForRaw.Execute(reader) +} + +func isRaw(mType string) bool { + switch mType { + case "image/x-tif": + case "image/x-canon-cr2": + case "image/x-canon-crw": + case "image/x-nikon-nef": + case "image/x-nikon-nrw": + case "image/x-sony-arw": + case "image/x-sony-sr2": + case "image/x-minolta-mrw": + case "image/x-minolta-mdc": + case "image/x-olympus-orf": + case "image/x-panasonic-rw2": + case "image/x-pentax-pef": + case "image/x-epson-erf": + case "image/x-raw": + case "image/x-x3f": + case "image/x-fuji-raf": + case "image/x-aptus-mos": + case "image/x-mamiya-mef": + case "image/x-hasselblad-3fr": + case "image/x-adobe-dng": + case "image/x-samsung-srw": + case "image/x-kodak-kdc": + case "image/x-kodak-dcr": + default: + return false + } + return true +} diff --git a/server/plugin/plg_image_thumbnail/resize_raw_linux_amd64.go b/server/plugin/plg_image_thumbnail/resize_raw_linux_amd64.go new file mode 100644 index 00000000..6932f9e2 --- /dev/null +++ b/server/plugin/plg_image_thumbnail/resize_raw_linux_amd64.go @@ -0,0 +1,11 @@ +package plg_image_thumbnail + +import ( + _ "embed" +) + +//go:embed dist/raw_linux_amd64.bin +var binaryThumbnailRaw []byte + +//go:embed dist/raw_linux_amd64.bin.sha256 +var checksumRaw []byte diff --git a/server/plugin/plg_image_thumbnail/resize_raw_linux_arm.go b/server/plugin/plg_image_thumbnail/resize_raw_linux_arm.go new file mode 100644 index 00000000..134057fb --- /dev/null +++ b/server/plugin/plg_image_thumbnail/resize_raw_linux_arm.go @@ -0,0 +1,11 @@ +package plg_image_thumbnail + +import ( + _ "embed" +) + +//go:embed dist/raw_linux_arm.bin +var binaryThumbnailRaw []byte + +//go:embed dist/raw_linux_arm.bin.sha256 +var checksumRaw []byte diff --git a/server/plugin/plg_image_thumbnail/src/image_jpeg.c b/server/plugin/plg_image_thumbnail/src/image_jpeg.c new file mode 100644 index 00000000..459cfdc1 --- /dev/null +++ b/server/plugin/plg_image_thumbnail/src/image_jpeg.c @@ -0,0 +1,217 @@ +#include +#include "utils.h" +#include "jpeglib.h" +// #include "webp/encode.h" + +#define JPEG_QUALITY 50 + +#define min(a, b) (a > b ? b : a) + +int jpeg_to_jpeg(FILE* input, FILE* output) { +#ifdef HAS_DEBUG + clock_t t; + t = clock(); +#endif + + struct jpeg_decompress_struct jpeg_config_input; + struct jpeg_compress_struct jpeg_config_output; + struct jpeg_error_mgr jerr; + int jpeg_row_stride; + int image_min_size; + JSAMPARRAY buffer; + + jpeg_config_input.err = jpeg_std_error(&jerr); + jpeg_config_output.err = jpeg_std_error(&jerr); + jpeg_config_input.dct_method = JDCT_IFAST; + jpeg_config_input.do_fancy_upsampling = FALSE; + jpeg_config_input.two_pass_quantize = FALSE; + jpeg_config_input.dither_mode = JDITHER_ORDERED; + + jpeg_create_decompress(&jpeg_config_input); + jpeg_create_compress(&jpeg_config_output); + jpeg_stdio_src(&jpeg_config_input, input); + jpeg_stdio_dest(&jpeg_config_output, output); + DEBUG("after constructor decompress"); + if(jpeg_read_header(&jpeg_config_input, TRUE) != JPEG_HEADER_OK) { + jpeg_destroy_decompress(&jpeg_config_input); + return 1; + } + DEBUG("after header read"); + jpeg_config_input.dct_method = JDCT_IFAST; + jpeg_config_input.do_fancy_upsampling = FALSE; + jpeg_config_input.two_pass_quantize = FALSE; + jpeg_config_input.dither_mode = JDITHER_ORDERED; + jpeg_calc_output_dimensions(&jpeg_config_input); + + image_min_size = min(jpeg_config_input.output_width, jpeg_config_input.output_height); + jpeg_config_input.scale_num = 1; + jpeg_config_input.scale_denom = 1; + if (image_min_size / 8 >= TARGET_SIZE) { + jpeg_config_input.scale_num = 1; + jpeg_config_input.scale_denom = 8; + } else if (image_min_size * 2 / 8 >= TARGET_SIZE) { + jpeg_config_input.scale_num = 1; + jpeg_config_input.scale_denom = 4; + } else if (image_min_size * 3 / 8 >= TARGET_SIZE) { + jpeg_config_input.scale_num = 3; + jpeg_config_input.scale_denom = 8; + } else if (image_min_size * 4 / 8 >= TARGET_SIZE) { + jpeg_config_input.scale_num = 4; + jpeg_config_input.scale_denom = 8; + } else if (image_min_size * 5 / 8 >= TARGET_SIZE) { + jpeg_config_input.scale_num = 5; + jpeg_config_input.scale_denom = 8; + } else if (image_min_size * 6 / 8 >= TARGET_SIZE) { + jpeg_config_input.scale_num = 6; + jpeg_config_input.scale_denom = 8; + } else if (image_min_size * 7 / 8 >= TARGET_SIZE) { + jpeg_config_input.scale_num = 7; + jpeg_config_input.scale_denom = 8; + } + + DEBUG("start decompress"); + if(jpeg_start_decompress(&jpeg_config_input) == FALSE) { + jpeg_destroy_decompress(&jpeg_config_input); + return 1; + } + DEBUG("processing image"); + jpeg_row_stride = jpeg_config_input.output_width * jpeg_config_input.output_components; + jpeg_config_output.image_width = jpeg_config_input.output_width; + jpeg_config_output.image_height = jpeg_config_input.output_height; + jpeg_config_output.input_components = jpeg_config_input.num_components; + jpeg_config_output.in_color_space = JCS_RGB; + jpeg_set_defaults(&jpeg_config_output); + jpeg_set_quality(&jpeg_config_output, JPEG_QUALITY, TRUE); + jpeg_start_compress(&jpeg_config_output, TRUE); + + buffer = (*jpeg_config_input.mem->alloc_sarray) ((j_common_ptr) &jpeg_config_input, JPOOL_IMAGE, jpeg_row_stride, 1); + while (jpeg_config_input.output_scanline < jpeg_config_input.output_height) { + // TODO: scanlines should return 1 + jpeg_read_scanlines(&jpeg_config_input, buffer, 1); + jpeg_write_scanlines(&jpeg_config_output, buffer, 1); + } + DEBUG("end decompress"); + jpeg_finish_decompress(&jpeg_config_input); + jpeg_destroy_decompress(&jpeg_config_input); + DEBUG("finish decompress"); + jpeg_finish_compress(&jpeg_config_output); + DEBUG("final"); + return 0; +} + +/* +static int MyWriter(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 jpeg_to_webp(FILE* input, FILE* output) { +#ifdef HAS_DEBUG + clock_t t; + t = clock(); +#endif + + struct jpeg_decompress_struct jpeg_config_input; + struct jpeg_error_mgr jerr; + u_int8_t* volatile rgb = NULL; + JSAMPROW buffer[1]; + int jpeg_row_stride; + + jpeg_config_input.err = jpeg_std_error(&jerr); + jpeg_config_input.dct_method = JDCT_IFAST; + jpeg_config_input.do_fancy_upsampling = FALSE; + jpeg_config_input.two_pass_quantize = FALSE; + jpeg_config_input.dither_mode = JDITHER_ORDERED; + + jpeg_create_decompress(&jpeg_config_input); + jpeg_stdio_src(&jpeg_config_input, input); + + DEBUG("after constructor decompress"); + if(jpeg_read_header(&jpeg_config_input, TRUE) != JPEG_HEADER_OK) { + jpeg_destroy_decompress(&jpeg_config_input); + return 1; + } + DEBUG("after header read"); + jpeg_config_input.dct_method = JDCT_IFAST; + jpeg_config_input.do_fancy_upsampling = FALSE; + jpeg_config_input.two_pass_quantize = FALSE; + jpeg_config_input.dither_mode = JDITHER_ORDERED; + jpeg_calc_output_dimensions(&jpeg_config_input); + DEBUG("start decompress"); + if(jpeg_start_decompress(&jpeg_config_input) == FALSE) { + jpeg_destroy_decompress(&jpeg_config_input); + return 1; + } + DEBUG("hot"); + jpeg_row_stride = jpeg_config_input.output_width * jpeg_config_input.output_components; + + rgb = (u_int8_t*)malloc(jpeg_config_input.output_height * jpeg_row_stride); + buffer[0] = (JSAMPLE*)rgb; + while (jpeg_config_input.output_scanline < jpeg_config_input.output_height) { + jpeg_read_scanlines(&jpeg_config_input, buffer, 1); + buffer[0] += jpeg_row_stride; + } + DEBUG("end decompress"); + jpeg_finish_decompress(&jpeg_config_input); + jpeg_destroy_decompress(&jpeg_config_input); + + DEBUG("finish decompress"); + + //////////////////////////////////////////////////////////////// + // ENCODE + // resize: https://chromium.googlesource.com/webm/libwebp/+/0.2.0/examples/cwebp.c#1174 + WebPPicture picture; + + if (!WebPPictureInit(&picture)) { + DEBUG("ERR picture init"); + return 1; + } + + picture.width = jpeg_config_input.output_width; + picture.height = jpeg_config_input.output_height; + if(!WebPPictureAlloc(&picture)) { + DEBUG("ALLOC ERR"); + return 1; + } + WebPPictureImportRGB(&picture, rgb, jpeg_row_stride); + + WebPConfig webp_config_output; + picture.writer = MyWriter; + picture.custom_ptr = output; + if (!WebPConfigInit(&webp_config_output)) { + DEBUG("ERR config init"); + return 1; + } + webp_config_output.image_hint = WEBP_HINT_PHOTO; + webp_config_output.method = 0; + + if (!WebPValidateConfig(&webp_config_output)) { + DEBUG("ERR WEB VALIDATION"); + } + fprintf(stderr, "rescale start %F\n", ((double)clock() - t)/CLOCKS_PER_SEC * 1000); + if (!WebPPictureRescale(&picture, jpeg_config_input.output_width / 4, jpeg_config_input.output_height / 4)) { + DEBUG("ERR Rescale"); + } + DEBUG("encoder start"); + WebPEncode(&webp_config_output, &picture); + DEBUG("encoder done"); + WebPPictureFree(&picture); + DEBUG("everything is free"); +} +*/ +void jpeg_size(FILE* infile, int* height, int* width) { + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + cinfo.err = jpeg_std_error(&jerr); + + jpeg_create_decompress(&cinfo); + jpeg_stdio_src(&cinfo, infile); + jpeg_read_header(&cinfo, TRUE); + jpeg_start_decompress(&cinfo); + + *width = cinfo.image_width; + *height = cinfo.image_height; + + jpeg_destroy_decompress(&cinfo); +} diff --git a/server/plugin/plg_image_thumbnail/src/image_jpeg.h b/server/plugin/plg_image_thumbnail/src/image_jpeg.h new file mode 100644 index 00000000..5fea405c --- /dev/null +++ b/server/plugin/plg_image_thumbnail/src/image_jpeg.h @@ -0,0 +1,10 @@ +#include +#include "jpeglib.h" +#include "utils.h" + +void jpeg_size(FILE* infile, int* height, int* width); + +int jpeg_to_jpeg(FILE* input, FILE* output); + +int jpeg_to_webp(FILE* input, FILE* output); + diff --git a/server/plugin/plg_image_thumbnail/src/png_to_webp.c b/server/plugin/plg_image_thumbnail/src/image_png.c similarity index 65% rename from server/plugin/plg_image_thumbnail/src/png_to_webp.c rename to server/plugin/plg_image_thumbnail/src/image_png.c index cfb92f01..82bc3858 100644 --- a/server/plugin/plg_image_thumbnail/src/png_to_webp.c +++ b/server/plugin/plg_image_thumbnail/src/image_png.c @@ -8,10 +8,8 @@ static int MyWriter(const uint8_t* data, size_t data_size, const WebPPicture* co return data_size ? (fwrite(data, data_size, 1, out) == 1) : 1; } -int main(int argc, const char **argv) { - FILE* input = stdin; - FILE* output = stdout; - WebPPicture picture; +int png_to_webp(FILE* input, FILE* output) { + WebPPicture picture; #ifdef HAS_DEBUG clock_t t; @@ -20,12 +18,12 @@ int main(int argc, const char **argv) { png_image image; memset(&image, 0, sizeof image); image.version = PNG_IMAGE_VERSION; - DEBUG("> reading png"); + DEBUG("reading png"); if (!png_image_begin_read_from_stdio(&image, input)) { ERROR("png_image_begin_read_from_stdio"); return 1; } - DEBUG("> allocate"); + DEBUG("allocate"); png_bytep buffer; image.format = PNG_FORMAT_RGBA; buffer = malloc(PNG_IMAGE_SIZE(image)); @@ -34,7 +32,7 @@ int main(int argc, const char **argv) { png_image_free(&image); return 1; } - DEBUG("> start reading"); + DEBUG("start reading"); if (!png_image_finish_read(&image, NULL, buffer, 0, NULL)) { ERROR("png_image_finish_read"); png_image_free(&image); @@ -44,7 +42,7 @@ int main(int argc, const char **argv) { ///////////////////////////////////////////// // encode to webp - DEBUG("> start encoding"); + DEBUG("start encoding"); if (!WebPPictureInit(&picture)) { ERROR("WebPPictureInit"); png_image_free(&image); @@ -59,7 +57,7 @@ int main(int argc, const char **argv) { free(buffer); return 1; } - DEBUG("> start encoding import"); + DEBUG("start encoding import"); WebPPictureImportRGBA(&picture, buffer, PNG_IMAGE_ROW_STRIDE(image)); png_image_free(&image); free(buffer); @@ -67,7 +65,7 @@ int main(int argc, const char **argv) { WebPConfig webp_config_output; picture.writer = MyWriter; picture.custom_ptr = output; - DEBUG("> start encoding config init"); + DEBUG("start encoding config init"); if (!WebPConfigInit(&webp_config_output)) { ERROR("ERR config init"); WebPPictureFree(&picture); @@ -80,7 +78,7 @@ int main(int argc, const char **argv) { WebPPictureFree(&picture); return 1; } - DEBUG("> rescale start"); + DEBUG("rescale start"); if (image.width > TARGET_SIZE && image.height > TARGET_SIZE) { float ratioHeight = (float) image.height / TARGET_SIZE; float ratioWidth = (float) image.width / TARGET_SIZE; @@ -91,10 +89,54 @@ int main(int argc, const char **argv) { return 1; } } - DEBUG("> encoder start"); + DEBUG("encoder start"); WebPEncode(&webp_config_output, &picture); - DEBUG("> encoder done"); + DEBUG("encoder done"); WebPPictureFree(&picture); - DEBUG("> cleaning up"); + DEBUG("cleaning up"); + return 0; +} + +int png_to_png(FILE* input, FILE* output) { +#ifdef HAS_DEBUG + clock_t t; + t = clock(); +#endif + 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; } diff --git a/server/plugin/plg_image_thumbnail/src/image_png.h b/server/plugin/plg_image_thumbnail/src/image_png.h new file mode 100644 index 00000000..e3cb3ad9 --- /dev/null +++ b/server/plugin/plg_image_thumbnail/src/image_png.h @@ -0,0 +1,5 @@ +#include + +int png_to_webp(FILE* input, FILE* output); + +int png_to_png(FILE* input, FILE* output); diff --git a/server/plugin/plg_image_thumbnail/src/image_raw.c b/server/plugin/plg_image_thumbnail/src/image_raw.c new file mode 100644 index 00000000..ef2ae009 --- /dev/null +++ b/server/plugin/plg_image_thumbnail/src/image_raw.c @@ -0,0 +1,82 @@ +#include +#include +#include +#include "utils.h" +#include "image_jpeg.h" + +#define BUF_SIZE 1024 * 1024 + +int raw_to_jpeg(FILE* input, FILE* output) { +#ifdef HAS_DEBUG + clock_t t; + t = clock(); +#endif + + char fname_in[32] = "/tmp/filestash.XXXXXX"; + int _mkstemp_in = mkstemp(fname_in); + if (_mkstemp_in == -1) { + ERROR("mkstemp_in"); + return 1; + } + FILE* f_in = fdopen(_mkstemp_in, "w"); + if (f_in == NULL) { + remove(fname_in); + return 1; + } + + char content[BUF_SIZE]; + int read; + while ((read = fread(content, sizeof(char), BUF_SIZE, input))) { + fwrite(content, read, sizeof(char), f_in); + } + + DEBUG("libraw init"); + libraw_data_t *raw = libraw_init(0); + DEBUG("libraw open file"); + if (libraw_open_file(raw, fname_in) != 0) { + ERROR("libraw_open_file"); + libraw_close(raw); + fclose(f_in); + remove(fname_in); + return 1; + } + + raw->params.output_tiff = 1; + DEBUG("libraw unpack thumb"); + char fname_out[32] = "/tmp/filestash.XXXXXX"; + int _mkstemp_out = mkstemp(fname_out); + if (_mkstemp_out == -1) { + ERROR("mkstemp_out"); + remove(fname_in); + fclose(f_in); + return 1; + } + + if (libraw_unpack_thumb(raw) == 0 && raw->thumbnail.tformat == LIBRAW_THUMBNAIL_JPEG) { + DEBUG("has an embed thumbnail"); + if (libraw_dcraw_thumb_writer(raw, fname_out) == 0) { + DEBUG("process thumbnail"); + libraw_close(raw); + FILE* f_out = fdopen(_mkstemp_out, "r"); + int err = jpeg_to_jpeg(f_out, output); + fclose(f_out); + fclose(f_in); + remove(fname_in); + remove(fname_out); + DEBUG("process complete"); + return err; + } + } + + ERROR("not implemented - abort"); + // if (libraw_unpack(raw) != 0) DEBUG("HERE0"); + // if (libraw_dcraw_process(raw) != 0) DEBUG("HERE1"); + // if (libraw_dcraw_ppm_tiff_writer(raw, fname_out) != 0) DEBUG("HERE2"); + // if (libraw_dcraw_thumb_writer(raw, fname_out) != 0) DEBUG("HERE3"); + // DEBUG("HERE__"); + fclose(f_in); + remove(fname_in); + remove(fname_out); + libraw_close(raw); + return 1; +} diff --git a/server/plugin/plg_image_thumbnail/src/image_raw.h b/server/plugin/plg_image_thumbnail/src/image_raw.h new file mode 100644 index 00000000..d936a491 --- /dev/null +++ b/server/plugin/plg_image_thumbnail/src/image_raw.h @@ -0,0 +1,7 @@ +#include +#include +#include +#include "utils.h" +#include "image_jpeg.h" + +int raw_to_jpeg(FILE* input, FILE* output); diff --git a/server/plugin/plg_image_thumbnail/src/image_webp.c b/server/plugin/plg_image_thumbnail/src/image_webp.c new file mode 100644 index 00000000..9a05550d --- /dev/null +++ b/server/plugin/plg_image_thumbnail/src/image_webp.c @@ -0,0 +1,27 @@ +#include +#include "webp/decode.h" + +#define DEFAULT_SIZE 100 +#define STEP_SIZE 100 + +void webp_size(FILE* infile, int* height, int* width) { + uint8_t *buffer[DEFAULT_SIZE]; + size_t buffer_sz=DEFAULT_SIZE; + size_t i=0; + while (!feof(infile)) { + // buffer[i] = fgetc(infile); + fread(buffer, buffer_sz+1, sizeof(char), infile); + i++; + if (i >= buffer_sz) { + buffer_sz += STEP_SIZE; + void *tmp = buffer; + buffer = realloc(buffer, buffer_sz); + if (buffer == NULL) { + free(tmp); + break; + } + } + } + + // WebPGetInfo(buffer, buffer_sz, height, width); +} diff --git a/server/plugin/plg_image_thumbnail/src/image_webp.h b/server/plugin/plg_image_thumbnail/src/image_webp.h new file mode 100644 index 00000000..6df944f5 --- /dev/null +++ b/server/plugin/plg_image_thumbnail/src/image_webp.h @@ -0,0 +1,3 @@ +#include + +void webp_size(FILE* infile, int* height, int* width); diff --git a/server/plugin/plg_image_thumbnail/src/jpeg_to_jpeg.c b/server/plugin/plg_image_thumbnail/src/jpeg_to_jpeg.c deleted file mode 100644 index bf7c371b..00000000 --- a/server/plugin/plg_image_thumbnail/src/jpeg_to_jpeg.c +++ /dev/null @@ -1,102 +0,0 @@ -#include -#include "jpeglib.h" -#include "utils.h" - -#define JPEG_QUALITY 50 - -#define min(a, b) (a > b ? b : a) - -int main(int argc, char **argv) { - FILE* input = stdin; - FILE* output = stdout; - -#ifdef HAS_DEBUG - clock_t t; - t = clock(); -#endif - - struct jpeg_decompress_struct jpeg_config_input; - struct jpeg_compress_struct jpeg_config_output; - struct jpeg_error_mgr jerr; - int jpeg_row_stride; - int image_min_size; - JSAMPARRAY buffer; - - jpeg_config_input.err = jpeg_std_error(&jerr); - jpeg_config_output.err = jpeg_std_error(&jerr); - jpeg_config_input.dct_method = JDCT_IFAST; - jpeg_config_input.do_fancy_upsampling = FALSE; - jpeg_config_input.two_pass_quantize = FALSE; - jpeg_config_input.dither_mode = JDITHER_ORDERED; - - jpeg_create_decompress(&jpeg_config_input); - jpeg_create_compress(&jpeg_config_output); - jpeg_stdio_src(&jpeg_config_input, input); - jpeg_stdio_dest(&jpeg_config_output, output); - DEBUG("> after constructor decompress"); - if(jpeg_read_header(&jpeg_config_input, TRUE) != JPEG_HEADER_OK) { - jpeg_destroy_decompress(&jpeg_config_input); - return 1; - } - DEBUG("> after header read"); - jpeg_config_input.dct_method = JDCT_IFAST; - jpeg_config_input.do_fancy_upsampling = FALSE; - jpeg_config_input.two_pass_quantize = FALSE; - jpeg_config_input.dither_mode = JDITHER_ORDERED; - jpeg_calc_output_dimensions(&jpeg_config_input); - - image_min_size = min(jpeg_config_input.output_width, jpeg_config_input.output_height); - jpeg_config_input.scale_num = 1; - jpeg_config_input.scale_denom = 1; - if (image_min_size / 8 >= TARGET_SIZE) { - jpeg_config_input.scale_num = 1; - jpeg_config_input.scale_denom = 8; - } else if (image_min_size * 2 / 8 >= TARGET_SIZE) { - jpeg_config_input.scale_num = 1; - jpeg_config_input.scale_denom = 4; - } else if (image_min_size * 3 / 8 >= TARGET_SIZE) { - jpeg_config_input.scale_num = 3; - jpeg_config_input.scale_denom = 8; - } else if (image_min_size * 4 / 8 >= TARGET_SIZE) { - jpeg_config_input.scale_num = 4; - jpeg_config_input.scale_denom = 8; - } else if (image_min_size * 5 / 8 >= TARGET_SIZE) { - jpeg_config_input.scale_num = 5; - jpeg_config_input.scale_denom = 8; - } else if (image_min_size * 6 / 8 >= TARGET_SIZE) { - jpeg_config_input.scale_num = 6; - jpeg_config_input.scale_denom = 8; - } else if (image_min_size * 7 / 8 >= TARGET_SIZE) { - jpeg_config_input.scale_num = 7; - jpeg_config_input.scale_denom = 8; - } - - DEBUG("> start decompress"); - if(jpeg_start_decompress(&jpeg_config_input) == FALSE) { - jpeg_destroy_decompress(&jpeg_config_input); - return 1; - } - DEBUG("> processing image"); - jpeg_row_stride = jpeg_config_input.output_width * jpeg_config_input.output_components; - jpeg_config_output.image_width = jpeg_config_input.output_width; - jpeg_config_output.image_height = jpeg_config_input.output_height; - jpeg_config_output.input_components = jpeg_config_input.num_components; - jpeg_config_output.in_color_space = JCS_RGB; - jpeg_set_defaults(&jpeg_config_output); - jpeg_set_quality(&jpeg_config_output, JPEG_QUALITY, TRUE); - jpeg_start_compress(&jpeg_config_output, TRUE); - - buffer = (*jpeg_config_input.mem->alloc_sarray) ((j_common_ptr) &jpeg_config_input, JPOOL_IMAGE, jpeg_row_stride, 1); - while (jpeg_config_input.output_scanline < jpeg_config_input.output_height) { - // TODO: scanlines should return 1 - jpeg_read_scanlines(&jpeg_config_input, buffer, 1); - jpeg_write_scanlines(&jpeg_config_output, buffer, 1); - } - DEBUG("> end decompress"); - jpeg_finish_decompress(&jpeg_config_input); - jpeg_destroy_decompress(&jpeg_config_input); - DEBUG("> finish decompress"); - jpeg_finish_compress(&jpeg_config_output); - DEBUG("> final"); - return 0; -} diff --git a/server/plugin/plg_image_thumbnail/src/jpeg_to_jpeg.h b/server/plugin/plg_image_thumbnail/src/jpeg_to_jpeg.h new file mode 100644 index 00000000..b5cf1e60 --- /dev/null +++ b/server/plugin/plg_image_thumbnail/src/jpeg_to_jpeg.h @@ -0,0 +1,7 @@ +#include +#include "jpeglib.h" +#include "utils.h" + +#define JPEG_QUALITY 50 + +#define min(a, b) (a > b ? b : a) diff --git a/server/plugin/plg_image_thumbnail/src/jpeg_to_webp.c b/server/plugin/plg_image_thumbnail/src/jpeg_to_webp.c deleted file mode 100644 index a300c5c2..00000000 --- a/server/plugin/plg_image_thumbnail/src/jpeg_to_webp.c +++ /dev/null @@ -1,109 +0,0 @@ -#include -#include "webp/encode.h" -#include -#include -#include "jpeglib.h" -#include "utils.h" - -static int MyWriter(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 main() { - FILE* input = stdin; - FILE* output = stdout; - -#ifdef HAS_DEBUG - clock_t t; - t = clock(); -#endif - - struct jpeg_decompress_struct jpeg_config_input; - struct jpeg_error_mgr jerr; - u_int8_t* volatile rgb = NULL; - JSAMPROW buffer[1]; - int jpeg_row_stride; - - jpeg_config_input.err = jpeg_std_error(&jerr); - jpeg_config_input.dct_method = JDCT_IFAST; - jpeg_config_input.do_fancy_upsampling = FALSE; - jpeg_config_input.two_pass_quantize = FALSE; - jpeg_config_input.dither_mode = JDITHER_ORDERED; - - jpeg_create_decompress(&jpeg_config_input); - jpeg_stdio_src(&jpeg_config_input, input); - - DEBUG("- after constructor decompress"); - if(jpeg_read_header(&jpeg_config_input, TRUE) != JPEG_HEADER_OK) { - jpeg_destroy_decompress(&jpeg_config_input); - return 1; - } - DEBUG("- after header read"); - jpeg_config_input.dct_method = JDCT_IFAST; - jpeg_config_input.do_fancy_upsampling = FALSE; - jpeg_config_input.two_pass_quantize = FALSE; - jpeg_config_input.dither_mode = JDITHER_ORDERED; - jpeg_calc_output_dimensions(&jpeg_config_input); - DEBUG("- start decompress"); - if(jpeg_start_decompress(&jpeg_config_input) == FALSE) { - jpeg_destroy_decompress(&jpeg_config_input); - return 1; - } - DEBUG("- hot"); - jpeg_row_stride = jpeg_config_input.output_width * jpeg_config_input.output_components; - - rgb = (u_int8_t*)malloc(jpeg_config_input.output_height * jpeg_row_stride); - buffer[0] = (JSAMPLE*)rgb; - while (jpeg_config_input.output_scanline < jpeg_config_input.output_height) { - jpeg_read_scanlines(&jpeg_config_input, buffer, 1); - buffer[0] += jpeg_row_stride; - } - DEBUG("- end decompress"); - jpeg_finish_decompress(&jpeg_config_input); - jpeg_destroy_decompress(&jpeg_config_input); - - DEBUG("- finish decompress"); - - //////////////////////////////////////////////////////////////// - // ENCODE - // resize: https://chromium.googlesource.com/webm/libwebp/+/0.2.0/examples/cwebp.c#1174 - WebPPicture picture; - - if (!WebPPictureInit(&picture)) { - DEBUG("ERR picture init"); - return 1; - } - - picture.width = jpeg_config_input.output_width; - picture.height = jpeg_config_input.output_height; - if(!WebPPictureAlloc(&picture)) { - DEBUG("ALLOC ERR"); - return 1; - } - WebPPictureImportRGB(&picture, rgb, jpeg_row_stride); - - WebPConfig webp_config_output; - picture.writer = MyWriter; - picture.custom_ptr = output; - if (!WebPConfigInit(&webp_config_output)) { - DEBUG("ERR config init"); - return 1; - } - webp_config_output.image_hint = WEBP_HINT_PHOTO; - webp_config_output.method = 0; - - if (!WebPValidateConfig(&webp_config_output)) { - DEBUG("ERR WEB VALIDATION"); - } - fprintf(stderr, "- rescale start %F\n", ((double)clock() - t)/CLOCKS_PER_SEC * 1000); - if (!WebPPictureRescale(&picture, jpeg_config_input.output_width / 4, jpeg_config_input.output_height / 4)) { - DEBUG("ERR Rescale"); - } - DEBUG("- encoder start"); - WebPEncode(&webp_config_output, &picture); - DEBUG("- encoder done"); - WebPPictureFree(&picture); - DEBUG("- everything is free"); -} diff --git a/server/plugin/plg_image_thumbnail/src/main_jpeg_to_jpeg.c b/server/plugin/plg_image_thumbnail/src/main_jpeg_to_jpeg.c new file mode 100644 index 00000000..48f2bad9 --- /dev/null +++ b/server/plugin/plg_image_thumbnail/src/main_jpeg_to_jpeg.c @@ -0,0 +1,6 @@ +#include +#include "image_jpeg.h" + +int main(int argc, char **argv) { + return jpeg_to_jpeg(stdin, stdout); +} diff --git a/server/plugin/plg_image_thumbnail/src/main_png_to_webp.c b/server/plugin/plg_image_thumbnail/src/main_png_to_webp.c new file mode 100644 index 00000000..e66f300e --- /dev/null +++ b/server/plugin/plg_image_thumbnail/src/main_png_to_webp.c @@ -0,0 +1,6 @@ +#include +#include "image_png.h" + +int main(int argc, char **argv) { + return png_to_webp(stdin, stdout); +} diff --git a/server/plugin/plg_image_thumbnail/src/main_raw_to_jpeg.c b/server/plugin/plg_image_thumbnail/src/main_raw_to_jpeg.c new file mode 100644 index 00000000..83b191e9 --- /dev/null +++ b/server/plugin/plg_image_thumbnail/src/main_raw_to_jpeg.c @@ -0,0 +1,5 @@ +#include "image_raw.h" + +int main(int args, const char **argv) { + return raw_to_jpeg(stdin, stdout); +} diff --git a/server/plugin/plg_image_thumbnail/src/png_to_png.c b/server/plugin/plg_image_thumbnail/src/png_to_png.c deleted file mode 100644 index 8f795a31..00000000 --- a/server/plugin/plg_image_thumbnail/src/png_to_png.c +++ /dev/null @@ -1,53 +0,0 @@ -#include -#include -#include -#include -#include -#include "utils.h" - -int main(int argc, const char **argv) { - FILE* input = stdin; - FILE* output = stdout; - -#ifdef HAS_DEBUG - clock_t t; - t = clock(); -#endif - 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; -} diff --git a/server/plugin/plg_image_thumbnail/src/png_to_webp.h b/server/plugin/plg_image_thumbnail/src/png_to_webp.h new file mode 100644 index 00000000..bf01fa57 --- /dev/null +++ b/server/plugin/plg_image_thumbnail/src/png_to_webp.h @@ -0,0 +1,13 @@ +#include +#include +#include "webp/encode.h" +#include "utils.h" + +static int MyWriter(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(FILE* input, FILE* output) { + +} diff --git a/server/plugin/plg_image_thumbnail/src/test.c b/server/plugin/plg_image_thumbnail/src/test.c new file mode 100644 index 00000000..7afe052a --- /dev/null +++ b/server/plugin/plg_image_thumbnail/src/test.c @@ -0,0 +1,132 @@ +#include +#include +#include +#include +#include "utils.h" +#include "jpeg_to_jpeg.h" +#include "png_to_webp.h" +#include "jpeg.h" +// #include "webp.h" + +int strEndsWith(const char *s, const char *suff) { + size_t slen = strlen(s); + size_t sufflen = strlen(suff); + return slen >= sufflen && !memcmp(s + slen - sufflen, suff, sufflen); +} + +void test_jpeg_to_jpeg(const char* basefolder) { + struct dirent *de; + DIR *dp; + int n = 0; + clock_t t = clock(); + + DEBUG("=================="); + DEBUG("TEST: jpeg_to_jpeg"); + dp = opendir(basefolder); + assert(dp != NULL); + remove("/tmp/out.dat"); + while ((de = readdir (dp)) != NULL) { + if (strEndsWith(de->d_name, ".jpeg") == 0 && strEndsWith(de->d_name, ".jpg") == 0) { + continue; + } + // STEP1: setup the test + n += 1; + char input_fname[2048] = ""; + strcpy(input_fname, basefolder); + strcat(input_fname, de->d_name); + fprintf(stderr, "= Processing[%s]:\n", input_fname); + FILE* input = fopen(input_fname, "r"); + FILE* output = fopen("/tmp/out.dat", "w"); + + // STEP2: run the test + int ret = jpeg_to_jpeg(input, output); + fclose(input); + fclose(output); + + // STEP3: assertions + int width = -1; + int height = -1; + output = fopen("/tmp/out.dat", "r"); + jpeg_size(output, &width, &height); + fclose(output); + if (width < 0 || width > 800) { + fprintf(stderr, "%dx%d", width, height); + assert("width outside range" == NULL); + } + if (height < 0 || height > 800) { + fprintf(stderr, "height[%d]", height); + assert("height outside range" == NULL); + } + assert(ret == 0); + remove("/tmp/out.dat"); + } + assert(n > 0); + closedir(dp); +} + +void test_png_to_webp(const char* basefolder) { + struct dirent *de; + DIR *dp; + int n = 0; + clock_t t = clock(); + + DEBUG("=================="); + DEBUG("TEST: png_to_webp"); + dp = opendir(basefolder); + assert(dp != NULL); + remove("/tmp/out.dat"); + while ((de = readdir (dp)) != NULL) { + if (strEndsWith(de->d_name, ".png") == 0) { + continue; + } + // STEP1: setup the test + n += 1; + char input_fname[2048] = ""; + strcpy(input_fname, basefolder); + strcat(input_fname, de->d_name); + fprintf(stderr, "= Processing[%s]:\n", input_fname); + FILE* input = fopen(input_fname, "r"); + FILE* output = fopen("/tmp/out.dat", "w"); + + // STEP2: run the test + int ret = png_to_webp(input, output); + fclose(input); + fclose(output); + + // STEP3: assertions + int width = -1; + int height = -1; + output = fopen("/tmp/out.dat", "r"); + // webp_size(output, &width, &height); + fclose(output); + if (width < 0 || width > 800) { + fprintf(stderr, "%dx%d", width, height); + assert("width outside range" == NULL); + } + if (height < 0 || height > 800) { + fprintf(stderr, "height[%d]", height); + assert("height outside range" == NULL); + } + assert(ret == 0); + remove("/tmp/out.dat"); + } + assert(n > 0); + closedir(dp); +} + +void test_raw_to_jpeg(const char* basename) { + clock_t t = clock(); + DEBUG("=================="); + DEBUG("(TODO)TEST: raw_to_jpeg"); +} + +int main(int args, const char **argv) { + if (args != 2) { + ERROR("need path with pictures in argument"); + return 1; + } + + // test_jpeg_to_jpeg(argv[1]); + // test_png_to_webp(argv[1]); + // test_raw_to_jpeg(argv[1]); +} diff --git a/server/plugin/plg_image_thumbnail/src/utils.h b/server/plugin/plg_image_thumbnail/src/utils.h index c800d09c..8e32cd9f 100644 --- a/server/plugin/plg_image_thumbnail/src/utils.h +++ b/server/plugin/plg_image_thumbnail/src/utils.h @@ -1,12 +1,12 @@ -#define TARGET_SIZE 250 +#define TARGET_SIZE 200 #define HAS_DEBUG 1 #if HAS_DEBUG == 1 #include #include -#define DEBUG(r) (fprintf(stderr, r ": %.2Fms\n", ((double)clock() - t)/CLOCKS_PER_SEC * 1000)) +#define DEBUG(r) (fprintf(stderr, "[DEBUG::('" r "')(%.2Fms)]", ((double)clock() - t)/CLOCKS_PER_SEC * 1000)) #else #define DEBUG(r) ((void)0) #endif -#define ERROR(r) (fprintf(stderr, r)) +#define ERROR(r) (fprintf(stderr, "[ERROR:('" r "')]"))