mirror of
https://github.com/mickael-kerjean/filestash
synced 2025-12-06 16:32:31 +01:00
feature (plg_image_thumbnail): improvement over thumbnailing
This commit is contained in:
parent
a78e817657
commit
339e9486e5
35 changed files with 823 additions and 364 deletions
11
Makefile
11
Makefile
|
|
@ -1,12 +1,17 @@
|
||||||
all:
|
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:
|
build_init:
|
||||||
find server/plugin/plg_* -type f -name "install.sh" -exec {} \;
|
|
||||||
go generate -x ./server/...
|
go generate -x ./server/...
|
||||||
|
|
||||||
build_frontend:
|
build_frontend:
|
||||||
NODE_ENV=production npm run build
|
NODE_ENV=production npm run build
|
||||||
|
|
||||||
build_backend:
|
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/
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@
|
||||||
"crw": "image/x-canon-crw",
|
"crw": "image/x-canon-crw",
|
||||||
"css": "text/css",
|
"css": "text/css",
|
||||||
"csv": "text/csv",
|
"csv": "text/csv",
|
||||||
|
"dcm": "image/dicom",
|
||||||
"dcr": "image/x-kodak-dcr",
|
"dcr": "image/x-kodak-dcr",
|
||||||
"deb": "application/octet-stream",
|
"deb": "application/octet-stream",
|
||||||
"der": "application/x-x509-ca-cert",
|
"der": "application/x-x509-ca-cert",
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,38 @@
|
||||||
|
PLATFORM=linux
|
||||||
|
ARCH=amd64
|
||||||
|
|
||||||
all:
|
all:
|
||||||
@make jpeg
|
@make jpeg
|
||||||
@make png
|
@make png
|
||||||
|
@make raw
|
||||||
|
|
||||||
jpeg:
|
image_jpeg.o:
|
||||||
gcc -Wall src/jpeg_to_jpeg.c -o dist/jpeg.bin -l:libjpeg.a
|
gcc -Wall -c src/image_jpeg.c -o src/image_jpeg.o
|
||||||
|
|
||||||
png:
|
image_png.o:
|
||||||
gcc -Wall src/png_to_webp.c -o dist/png.bin -l:libpng.a -l:libz.a -l:libwebp.a -lpthread -lm
|
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
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -9,6 +9,9 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed dist/placeholder.png
|
//go:embed dist/placeholder.png
|
||||||
|
|
@ -17,6 +20,16 @@ var placeholder []byte
|
||||||
func init() {
|
func init() {
|
||||||
Hooks.Register.Thumbnailer("image/png", thumbnailBuilder{thumbnailPng})
|
Hooks.Register.Thumbnailer("image/png", thumbnailBuilder{thumbnailPng})
|
||||||
Hooks.Register.Thumbnailer("image/jpeg", thumbnailBuilder{thumbnailJpeg})
|
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) {
|
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) {
|
func thumbnailJpeg(reader io.ReadCloser, ctx *App, res *http.ResponseWriter, req *http.Request) (io.ReadCloser, error) {
|
||||||
h := (*res).Header()
|
h := (*res).Header()
|
||||||
r, err := createThumbnailForJpeg(reader)
|
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 {
|
if err != nil {
|
||||||
h.Set("Content-Type", "image/png")
|
h.Set("Content-Type", "image/png")
|
||||||
return NewReadCloserFromBytes(placeholder), nil
|
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)
|
return this.fn(reader, ctx, res, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupProgram(name string, raw []byte) error {
|
type ThumbnailExecutable struct {
|
||||||
p := "/tmp/" + name
|
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)
|
f, err := os.OpenFile(p, os.O_RDONLY, os.ModePerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
outFile, err := os.OpenFile(p, os.O_CREATE|os.O_WRONLY, os.ModePerm)
|
outFile, err := os.OpenFile(p, os.O_CREATE|os.O_WRONLY, os.ModePerm)
|
||||||
if err != nil {
|
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 {
|
if err = outFile.Close(); err != nil {
|
||||||
return err
|
Log.Warning("plg_image_thumbnail::init::run::close '%s'", this.Name)
|
||||||
}
|
return
|
||||||
f, err = os.OpenFile(p, os.O_RDONLY, os.ModePerm)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
b := make([]byte, 5)
|
if f.Close() != nil {
|
||||||
n, err := f.Read(b)
|
Log.Warning("plg_image_thumbnail::init::close '%s'", this.Name)
|
||||||
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")
|
|
||||||
}
|
}
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,19 @@
|
||||||
package plg_image_thumbnail
|
package plg_image_thumbnail
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
. "github.com/mickael-kerjean/filestash/server/common"
|
|
||||||
"io"
|
"io"
|
||||||
"os/exec"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func createThumbnailForJpeg(reader io.ReadCloser) (io.ReadCloser, error) {
|
var exeForJpeg ThumbnailExecutable = ThumbnailExecutable{
|
||||||
name := "thumbnail_web.bin"
|
Name: "thumbnail_jpeg.bin",
|
||||||
err := setupProgram(name, binaryThumbnailJpeg)
|
Binary: &binaryThumbnailJpeg,
|
||||||
if err != nil {
|
Checksum: checksumJpeg,
|
||||||
Log.Warning("plg_image_thumbnail %s")
|
|
||||||
reader.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var buf bytes.Buffer
|
func init() {
|
||||||
var errBuff bytes.Buffer
|
exeForJpeg.Init()
|
||||||
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()
|
func createThumbnailForJpeg(reader io.ReadCloser) (io.ReadCloser, error) {
|
||||||
return NewReadCloserFromBytes(buf.Bytes()), nil
|
return exeForJpeg.Execute(reader)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,3 +6,6 @@ import (
|
||||||
|
|
||||||
//go:embed dist/jpeg_linux_amd64.bin
|
//go:embed dist/jpeg_linux_amd64.bin
|
||||||
var binaryThumbnailJpeg []byte
|
var binaryThumbnailJpeg []byte
|
||||||
|
|
||||||
|
//go:embed dist/jpeg_linux_amd64.bin.sha256
|
||||||
|
var checksumJpeg []byte
|
||||||
|
|
|
||||||
|
|
@ -6,3 +6,6 @@ import (
|
||||||
|
|
||||||
//go:embed dist/jpeg_linux_arm.bin
|
//go:embed dist/jpeg_linux_arm.bin
|
||||||
var binaryThumbnailJpeg []byte
|
var binaryThumbnailJpeg []byte
|
||||||
|
|
||||||
|
//go:embed dist/jpeg_linux_arm.bin.sha256
|
||||||
|
var checksumJpeg []byte
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,19 @@
|
||||||
package plg_image_thumbnail
|
package plg_image_thumbnail
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
_ "embed"
|
|
||||||
"errors"
|
|
||||||
. "github.com/mickael-kerjean/filestash/server/common"
|
|
||||||
"io"
|
"io"
|
||||||
"os/exec"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func createThumbnailForPng(reader io.ReadCloser) (io.ReadCloser, error) {
|
var exeForPng ThumbnailExecutable = ThumbnailExecutable{
|
||||||
name := "thumbnail_webp.bin"
|
Name: "thumbnail_png.bin",
|
||||||
err := setupProgram(name, binaryThumbnailPng)
|
Binary: &binaryThumbnailPng,
|
||||||
if err != nil {
|
Checksum: checksumPng,
|
||||||
Log.Warning("plg_image_thumbnail %s")
|
|
||||||
reader.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var buf bytes.Buffer
|
func init() {
|
||||||
var errBuff bytes.Buffer
|
exeForPng.Init()
|
||||||
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()
|
func createThumbnailForPng(reader io.ReadCloser) (io.ReadCloser, error) {
|
||||||
return NewReadCloserFromBytes(buf.Bytes()), nil
|
return exeForPng.Execute(reader)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,3 +6,6 @@ import (
|
||||||
|
|
||||||
//go:embed dist/png_linux_amd64.bin
|
//go:embed dist/png_linux_amd64.bin
|
||||||
var binaryThumbnailPng []byte
|
var binaryThumbnailPng []byte
|
||||||
|
|
||||||
|
//go:embed dist/png_linux_amd64.bin.sha256
|
||||||
|
var checksumPng []byte
|
||||||
|
|
|
||||||
|
|
@ -6,3 +6,6 @@ import (
|
||||||
|
|
||||||
//go:embed dist/png_linux_arm.bin
|
//go:embed dist/png_linux_arm.bin
|
||||||
var binaryThumbnailPng []byte
|
var binaryThumbnailPng []byte
|
||||||
|
|
||||||
|
//go:embed dist/png_linux_arm.bin.sha256
|
||||||
|
var checksumPng []byte
|
||||||
|
|
|
||||||
50
server/plugin/plg_image_thumbnail/resize_raw.go
Normal file
50
server/plugin/plg_image_thumbnail/resize_raw.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
11
server/plugin/plg_image_thumbnail/resize_raw_linux_amd64.go
Normal file
11
server/plugin/plg_image_thumbnail/resize_raw_linux_amd64.go
Normal file
|
|
@ -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
|
||||||
11
server/plugin/plg_image_thumbnail/resize_raw_linux_arm.go
Normal file
11
server/plugin/plg_image_thumbnail/resize_raw_linux_arm.go
Normal file
|
|
@ -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
|
||||||
217
server/plugin/plg_image_thumbnail/src/image_jpeg.c
Normal file
217
server/plugin/plg_image_thumbnail/src/image_jpeg.c
Normal file
|
|
@ -0,0 +1,217 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#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);
|
||||||
|
}
|
||||||
10
server/plugin/plg_image_thumbnail/src/image_jpeg.h
Normal file
10
server/plugin/plg_image_thumbnail/src/image_jpeg.h
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#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);
|
||||||
|
|
||||||
|
|
@ -8,9 +8,7 @@ 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;
|
return data_size ? (fwrite(data, data_size, 1, out) == 1) : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, const char **argv) {
|
int png_to_webp(FILE* input, FILE* output) {
|
||||||
FILE* input = stdin;
|
|
||||||
FILE* output = stdout;
|
|
||||||
WebPPicture picture;
|
WebPPicture picture;
|
||||||
|
|
||||||
#ifdef HAS_DEBUG
|
#ifdef HAS_DEBUG
|
||||||
|
|
@ -20,12 +18,12 @@ int main(int argc, const char **argv) {
|
||||||
png_image image;
|
png_image image;
|
||||||
memset(&image, 0, sizeof image);
|
memset(&image, 0, sizeof image);
|
||||||
image.version = PNG_IMAGE_VERSION;
|
image.version = PNG_IMAGE_VERSION;
|
||||||
DEBUG("> reading png");
|
DEBUG("reading png");
|
||||||
if (!png_image_begin_read_from_stdio(&image, input)) {
|
if (!png_image_begin_read_from_stdio(&image, input)) {
|
||||||
ERROR("png_image_begin_read_from_stdio");
|
ERROR("png_image_begin_read_from_stdio");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
DEBUG("> allocate");
|
DEBUG("allocate");
|
||||||
png_bytep buffer;
|
png_bytep buffer;
|
||||||
image.format = PNG_FORMAT_RGBA;
|
image.format = PNG_FORMAT_RGBA;
|
||||||
buffer = malloc(PNG_IMAGE_SIZE(image));
|
buffer = malloc(PNG_IMAGE_SIZE(image));
|
||||||
|
|
@ -34,7 +32,7 @@ int main(int argc, const char **argv) {
|
||||||
png_image_free(&image);
|
png_image_free(&image);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
DEBUG("> start reading");
|
DEBUG("start reading");
|
||||||
if (!png_image_finish_read(&image, NULL, buffer, 0, NULL)) {
|
if (!png_image_finish_read(&image, NULL, buffer, 0, NULL)) {
|
||||||
ERROR("png_image_finish_read");
|
ERROR("png_image_finish_read");
|
||||||
png_image_free(&image);
|
png_image_free(&image);
|
||||||
|
|
@ -44,7 +42,7 @@ int main(int argc, const char **argv) {
|
||||||
|
|
||||||
/////////////////////////////////////////////
|
/////////////////////////////////////////////
|
||||||
// encode to webp
|
// encode to webp
|
||||||
DEBUG("> start encoding");
|
DEBUG("start encoding");
|
||||||
if (!WebPPictureInit(&picture)) {
|
if (!WebPPictureInit(&picture)) {
|
||||||
ERROR("WebPPictureInit");
|
ERROR("WebPPictureInit");
|
||||||
png_image_free(&image);
|
png_image_free(&image);
|
||||||
|
|
@ -59,7 +57,7 @@ int main(int argc, const char **argv) {
|
||||||
free(buffer);
|
free(buffer);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
DEBUG("> start encoding import");
|
DEBUG("start encoding import");
|
||||||
WebPPictureImportRGBA(&picture, buffer, PNG_IMAGE_ROW_STRIDE(image));
|
WebPPictureImportRGBA(&picture, buffer, PNG_IMAGE_ROW_STRIDE(image));
|
||||||
png_image_free(&image);
|
png_image_free(&image);
|
||||||
free(buffer);
|
free(buffer);
|
||||||
|
|
@ -67,7 +65,7 @@ int main(int argc, const char **argv) {
|
||||||
WebPConfig webp_config_output;
|
WebPConfig webp_config_output;
|
||||||
picture.writer = MyWriter;
|
picture.writer = MyWriter;
|
||||||
picture.custom_ptr = output;
|
picture.custom_ptr = output;
|
||||||
DEBUG("> start encoding config init");
|
DEBUG("start encoding config init");
|
||||||
if (!WebPConfigInit(&webp_config_output)) {
|
if (!WebPConfigInit(&webp_config_output)) {
|
||||||
ERROR("ERR config init");
|
ERROR("ERR config init");
|
||||||
WebPPictureFree(&picture);
|
WebPPictureFree(&picture);
|
||||||
|
|
@ -80,7 +78,7 @@ int main(int argc, const char **argv) {
|
||||||
WebPPictureFree(&picture);
|
WebPPictureFree(&picture);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
DEBUG("> rescale start");
|
DEBUG("rescale start");
|
||||||
if (image.width > TARGET_SIZE && image.height > TARGET_SIZE) {
|
if (image.width > TARGET_SIZE && image.height > TARGET_SIZE) {
|
||||||
float ratioHeight = (float) image.height / TARGET_SIZE;
|
float ratioHeight = (float) image.height / TARGET_SIZE;
|
||||||
float ratioWidth = (float) image.width / TARGET_SIZE;
|
float ratioWidth = (float) image.width / TARGET_SIZE;
|
||||||
|
|
@ -91,10 +89,54 @@ int main(int argc, const char **argv) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DEBUG("> encoder start");
|
DEBUG("encoder start");
|
||||||
WebPEncode(&webp_config_output, &picture);
|
WebPEncode(&webp_config_output, &picture);
|
||||||
DEBUG("> encoder done");
|
DEBUG("encoder done");
|
||||||
WebPPictureFree(&picture);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
5
server/plugin/plg_image_thumbnail/src/image_png.h
Normal file
5
server/plugin/plg_image_thumbnail/src/image_png.h
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int png_to_webp(FILE* input, FILE* output);
|
||||||
|
|
||||||
|
int png_to_png(FILE* input, FILE* output);
|
||||||
82
server/plugin/plg_image_thumbnail/src/image_raw.c
Normal file
82
server/plugin/plg_image_thumbnail/src/image_raw.c
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <libraw/libraw.h>
|
||||||
|
#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;
|
||||||
|
}
|
||||||
7
server/plugin/plg_image_thumbnail/src/image_raw.h
Normal file
7
server/plugin/plg_image_thumbnail/src/image_raw.h
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <libraw/libraw.h>
|
||||||
|
#include "utils.h"
|
||||||
|
#include "image_jpeg.h"
|
||||||
|
|
||||||
|
int raw_to_jpeg(FILE* input, FILE* output);
|
||||||
27
server/plugin/plg_image_thumbnail/src/image_webp.c
Normal file
27
server/plugin/plg_image_thumbnail/src/image_webp.c
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#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);
|
||||||
|
}
|
||||||
3
server/plugin/plg_image_thumbnail/src/image_webp.h
Normal file
3
server/plugin/plg_image_thumbnail/src/image_webp.h
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
void webp_size(FILE* infile, int* height, int* width);
|
||||||
|
|
@ -1,102 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
7
server/plugin/plg_image_thumbnail/src/jpeg_to_jpeg.h
Normal file
7
server/plugin/plg_image_thumbnail/src/jpeg_to_jpeg.h
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "jpeglib.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
#define JPEG_QUALITY 50
|
||||||
|
|
||||||
|
#define min(a, b) (a > b ? b : a)
|
||||||
|
|
@ -1,109 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include "webp/encode.h"
|
|
||||||
#include <time.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#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");
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "image_jpeg.h"
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
return jpeg_to_jpeg(stdin, stdout);
|
||||||
|
}
|
||||||
6
server/plugin/plg_image_thumbnail/src/main_png_to_webp.c
Normal file
6
server/plugin/plg_image_thumbnail/src/main_png_to_webp.c
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "image_png.h"
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
return png_to_webp(stdin, stdout);
|
||||||
|
}
|
||||||
5
server/plugin/plg_image_thumbnail/src/main_raw_to_jpeg.c
Normal file
5
server/plugin/plg_image_thumbnail/src/main_raw_to_jpeg.c
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
#include "image_raw.h"
|
||||||
|
|
||||||
|
int main(int args, const char **argv) {
|
||||||
|
return raw_to_jpeg(stdin, stdout);
|
||||||
|
}
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <png.h>
|
|
||||||
#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;
|
|
||||||
}
|
|
||||||
13
server/plugin/plg_image_thumbnail/src/png_to_webp.h
Normal file
13
server/plugin/plg_image_thumbnail/src/png_to_webp.h
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
#include <string.h>
|
||||||
|
#include <png.h>
|
||||||
|
#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) {
|
||||||
|
|
||||||
|
}
|
||||||
132
server/plugin/plg_image_thumbnail/src/test.c
Normal file
132
server/plugin/plg_image_thumbnail/src/test.c
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#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]);
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
#define TARGET_SIZE 250
|
#define TARGET_SIZE 200
|
||||||
|
|
||||||
#define HAS_DEBUG 1
|
#define HAS_DEBUG 1
|
||||||
#if HAS_DEBUG == 1
|
#if HAS_DEBUG == 1
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#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
|
#else
|
||||||
#define DEBUG(r) ((void)0)
|
#define DEBUG(r) ((void)0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define ERROR(r) (fprintf(stderr, r))
|
#define ERROR(r) (fprintf(stderr, "[ERROR:('" r "')]"))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue