feature (plg_image_thumbnail): improvement over thumbnailing

This commit is contained in:
Mickael Kerjean 2022-12-05 00:33:12 +11:00
parent a78e817657
commit 339e9486e5
35 changed files with 823 additions and 364 deletions

View file

@ -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/

View file

@ -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",

View file

@ -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

View file

@ -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
} }

View file

@ -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)
} }

View file

@ -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

View file

@ -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

View file

@ -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)
} }

View file

@ -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

View file

@ -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

View 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
}

View 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

View 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

View 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);
}

View 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);

View file

@ -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;
} }

View file

@ -0,0 +1,5 @@
#include <stdio.h>
int png_to_webp(FILE* input, FILE* output);
int png_to_png(FILE* input, FILE* output);

View 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;
}

View 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);

View 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);
}

View file

@ -0,0 +1,3 @@
#include <stdio.h>
void webp_size(FILE* infile, int* height, int* width);

View file

@ -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;
}

View 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)

View file

@ -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");
}

View file

@ -0,0 +1,6 @@
#include <stdio.h>
#include "image_jpeg.h"
int main(int argc, char **argv) {
return jpeg_to_jpeg(stdin, stdout);
}

View file

@ -0,0 +1,6 @@
#include <stdio.h>
#include "image_png.h"
int main(int argc, char **argv) {
return png_to_webp(stdin, stdout);
}

View file

@ -0,0 +1,5 @@
#include "image_raw.h"
int main(int args, const char **argv) {
return raw_to_jpeg(stdin, stdout);
}

View file

@ -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;
}

View 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) {
}

View 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]);
}

View file

@ -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 "')]"))