* Get rid of all depenence on libgd, and use libjpeg directly.
This commit is contained in:
parent
935225e1a3
commit
ac5a37b27a
4
INSTALL
4
INSTALL
@ -1,12 +1,12 @@
|
|||||||
MiniDLNA project.
|
MiniDLNA project.
|
||||||
(c) 2008 Justin Maggard
|
(c) 2009 Justin Maggard
|
||||||
Parts (c) 2006-2008 Thomas Bernard
|
Parts (c) 2006-2008 Thomas Bernard
|
||||||
Homepage : http://sourceforge.net/projects/minidlna/
|
Homepage : http://sourceforge.net/projects/minidlna/
|
||||||
|
|
||||||
Prerequisites :
|
Prerequisites :
|
||||||
|
|
||||||
- libexif
|
- libexif
|
||||||
- libgd2 (either libgd2-xpm or libgd2-noxpm)
|
- libjpeg
|
||||||
- libid3tag
|
- libid3tag
|
||||||
- libFLAC
|
- libFLAC
|
||||||
- libvorbis
|
- libvorbis
|
||||||
|
24
Makefile
24
Makefile
@ -28,20 +28,20 @@ BASEOBJS = minidlna.o upnphttp.o upnpdescgen.o upnpsoap.o \
|
|||||||
upnpreplyparse.o minixml.o \
|
upnpreplyparse.o minixml.o \
|
||||||
getifaddr.o daemonize.o upnpglobalvars.o \
|
getifaddr.o daemonize.o upnpglobalvars.o \
|
||||||
options.o minissdp.o upnpevents.o \
|
options.o minissdp.o upnpevents.o \
|
||||||
sql.o utils.o metadata.o albumart.o scanner.o inotify.o \
|
sql.o utils.o metadata.o scanner.o inotify.o \
|
||||||
tivo_utils.o tivo_beacon.o tivo_commands.o \
|
tivo_utils.o tivo_beacon.o tivo_commands.o \
|
||||||
tagutils/textutils.o tagutils/misc.o tagutils/tagutils.o \
|
tagutils/textutils.o tagutils/misc.o tagutils/tagutils.o \
|
||||||
log.o
|
image_utils.o albumart.o log.o
|
||||||
|
|
||||||
ALLOBJS = $(BASEOBJS) $(LNXOBJS)
|
ALLOBJS = $(BASEOBJS) $(LNXOBJS)
|
||||||
|
|
||||||
LIBS = -lexif -ljpeg -lsqlite3 -lavformat -lid3tag -lFLAC -lvorbis -luuid -lgd
|
LIBS = -lexif -ljpeg -lsqlite3 -lavformat -lid3tag -lFLAC -lvorbis -luuid
|
||||||
|
|
||||||
TESTUPNPDESCGENOBJS = testupnpdescgen.o upnpdescgen.o
|
TESTUPNPDESCGENOBJS = testupnpdescgen.o upnpdescgen.o
|
||||||
|
|
||||||
EXECUTABLES = minidlna testupnpdescgen
|
EXECUTABLES = minidlna testupnpdescgen
|
||||||
|
|
||||||
.PHONY: all clean install depend genuuid
|
.PHONY: all clean install depend
|
||||||
|
|
||||||
all: $(EXECUTABLES)
|
all: $(EXECUTABLES)
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ clean:
|
|||||||
$(RM) $(EXECUTABLES)
|
$(RM) $(EXECUTABLES)
|
||||||
$(RM) testupnpdescgen.o
|
$(RM) testupnpdescgen.o
|
||||||
|
|
||||||
install: minidlna genuuid
|
install: minidlna
|
||||||
$(INSTALL) -d $(SBININSTALLDIR)
|
$(INSTALL) -d $(SBININSTALLDIR)
|
||||||
$(INSTALL) minidlna $(SBININSTALLDIR)
|
$(INSTALL) minidlna $(SBININSTALLDIR)
|
||||||
$(INSTALL) -d $(ETCINSTALLDIR)
|
$(INSTALL) -d $(ETCINSTALLDIR)
|
||||||
@ -60,11 +60,6 @@ install: minidlna genuuid
|
|||||||
$(INSTALL) -d $(PREFIX)/etc/init.d
|
$(INSTALL) -d $(PREFIX)/etc/init.d
|
||||||
$(INSTALL) linux/miniupnpd.init.d.script $(PREFIX)/etc/init.d/miniupnpd
|
$(INSTALL) linux/miniupnpd.init.d.script $(PREFIX)/etc/init.d/miniupnpd
|
||||||
|
|
||||||
# genuuid is using the uuidgen CLI tool which is part of libuuid
|
|
||||||
# from the e2fsprogs
|
|
||||||
genuuid:
|
|
||||||
sed -i -e "s/^uuid=[-0-9a-f]*/uuid=`(genuuid||uuidgen) 2>/dev/null`/" minidlna.conf
|
|
||||||
|
|
||||||
minidlna: $(BASEOBJS) $(LNXOBJS) $(LIBS)
|
minidlna: $(BASEOBJS) $(LNXOBJS) $(LIBS)
|
||||||
|
|
||||||
testupnpdescgen: $(TESTUPNPDESCGENOBJS)
|
testupnpdescgen: $(TESTUPNPDESCGENOBJS)
|
||||||
@ -83,7 +78,7 @@ minidlna.o: upnphttp.h upnpdescgen.h minidlnapath.h getifaddr.h upnpsoap.h
|
|||||||
minidlna.o: options.h minissdp.h daemonize.h upnpevents.h
|
minidlna.o: options.h minissdp.h daemonize.h upnpevents.h
|
||||||
minidlna.o: commonrdr.h log.h
|
minidlna.o: commonrdr.h log.h
|
||||||
upnphttp.o: config.h upnphttp.h upnpdescgen.h minidlnapath.h upnpsoap.h
|
upnphttp.o: config.h upnphttp.h upnpdescgen.h minidlnapath.h upnpsoap.h
|
||||||
upnphttp.o: upnpevents.h log.h
|
upnphttp.o: upnpevents.h image_utils.h sql.h log.h
|
||||||
upnpdescgen.o: config.h upnpdescgen.h minidlnapath.h upnpglobalvars.h
|
upnpdescgen.o: config.h upnpdescgen.h minidlnapath.h upnpglobalvars.h
|
||||||
upnpdescgen.o: minidlnatypes.h upnpdescstrings.h log.h
|
upnpdescgen.o: minidlnatypes.h upnpdescstrings.h log.h
|
||||||
upnpsoap.o: config.h upnpglobalvars.h minidlnatypes.h log.h utils.h sql.h
|
upnpsoap.o: config.h upnpglobalvars.h minidlnatypes.h log.h utils.h sql.h
|
||||||
@ -105,8 +100,13 @@ upnpdescgen.o: config.h upnpdescgen.h minidlnapath.h upnpglobalvars.h
|
|||||||
upnpdescgen.o: minidlnatypes.h upnpdescstrings.h
|
upnpdescgen.o: minidlnatypes.h upnpdescstrings.h
|
||||||
scanner.o: upnpglobalvars.h metadata.h utils.h sql.h scanner.h log.h
|
scanner.o: upnpglobalvars.h metadata.h utils.h sql.h scanner.h log.h
|
||||||
metadata.o: upnpglobalvars.h metadata.h albumart.h utils.h sql.h log.h
|
metadata.o: upnpglobalvars.h metadata.h albumart.h utils.h sql.h log.h
|
||||||
|
albumart.o: upnpglobalvars.h albumart.h utils.h image_utils.h sql.h log.h
|
||||||
tagutils/misc.o: tagutils/misc.h
|
tagutils/misc.o: tagutils/misc.h
|
||||||
tagutils/textutils.o: tagutils/misc.h tagutils/textutils.h log.h
|
tagutils/textutils.o: tagutils/misc.h tagutils/textutils.h log.h
|
||||||
tagutils/tagutils.o: tagutils/tagutils-asf.c tagutils/tagutils-flc.c tagutils/tagutils-plist.c tagutils/tagutils-aac.c tagutils/tagutils-asf.h tagutils/tagutils-flc.h tagutils/tagutils-mp3.c tagutils/tagutils-ogg.c tagutils/tagutils-aac.h tagutils/tagutils.h tagutils/tagutils-mp3.h tagutils/tagutils-ogg.h log.h
|
tagutils/tagutils.o: tagutils/tagutils-asf.c tagutils/tagutils-flc.c tagutils/tagutils-plist.c
|
||||||
|
tagutils/tagutils.o: tagutils/tagutils-aac.c tagutils/tagutils-asf.h tagutils/tagutils-flc.h tagutils/tagutils-mp3.c
|
||||||
|
tagutils/tagutils.o: tagutils/tagutils-ogg.c tagutils/tagutils-aac.h tagutils/tagutils.h tagutils/tagutils-mp3.h tagutils/tagutils-ogg.h log.h
|
||||||
|
image_utils.o: image_utils.h
|
||||||
|
utils.o: utils.h
|
||||||
sql.o: sql.h
|
sql.o: sql.h
|
||||||
log.o: log.h
|
log.o: log.h
|
||||||
|
2
README
2
README
@ -1,5 +1,5 @@
|
|||||||
MiniDLNA project
|
MiniDLNA project
|
||||||
(c) 2008 Justin Maggard
|
(c) 2009 Justin Maggard
|
||||||
Parts (c) 2006-2007 Thomas Bernard
|
Parts (c) 2006-2007 Thomas Bernard
|
||||||
webpage: http://sourceforge.net/projects/minidlna/
|
webpage: http://sourceforge.net/projects/minidlna/
|
||||||
|
|
||||||
|
188
albumart.c
188
albumart.c
@ -25,51 +25,24 @@
|
|||||||
#include <setjmp.h>
|
#include <setjmp.h>
|
||||||
|
|
||||||
#include <jpeglib.h>
|
#include <jpeglib.h>
|
||||||
#include <gd.h>
|
|
||||||
|
|
||||||
#include "upnpglobalvars.h"
|
#include "upnpglobalvars.h"
|
||||||
#include "sql.h"
|
#include "sql.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
#include "image_utils.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
/* For libjpeg error handling */
|
|
||||||
jmp_buf setjmp_buffer;
|
|
||||||
static void libjpeg_error_handler(j_common_ptr cinfo)
|
|
||||||
{
|
|
||||||
cinfo->err->output_message (cinfo);
|
|
||||||
longjmp(setjmp_buffer, 1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0 // Not needed currently
|
|
||||||
int
|
|
||||||
check_res(int width, int height, char * dlna_pn)
|
|
||||||
{
|
|
||||||
if( (width <= 0) || (height <= 0) )
|
|
||||||
return 0;
|
|
||||||
if( width <= 160 && height <= 160 )
|
|
||||||
strcpy(dlna_pn, "JPEG_TN");
|
|
||||||
else if( width <= 640 && height <= 480 )
|
|
||||||
strcpy(dlna_pn, "JPEG_SM");
|
|
||||||
else if( width <= 1024 && height <= 768 )
|
|
||||||
strcpy(dlna_pn, "JPEG_MED");
|
|
||||||
else if( width <= 4096 && height <= 4096 )
|
|
||||||
strcpy(dlna_pn, "JPEG_LRG");
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
char *
|
char *
|
||||||
save_resized_album_art(void * ptr, const char * path, int srcw, int srch, int file, int size)
|
save_resized_album_art(image * imsrc, const char * path)
|
||||||
{
|
{
|
||||||
FILE *dstfile;
|
|
||||||
gdImagePtr imsrc = 0, imdst = 0;
|
|
||||||
int dstw, dsth;
|
int dstw, dsth;
|
||||||
|
image * imdst;
|
||||||
char * cache_file;
|
char * cache_file;
|
||||||
char * cache_dir;
|
char * cache_dir;
|
||||||
|
|
||||||
|
if( !imsrc )
|
||||||
|
return NULL;
|
||||||
|
|
||||||
asprintf(&cache_file, DB_PATH "/art_cache%s", path);
|
asprintf(&cache_file, DB_PATH "/art_cache%s", path);
|
||||||
if( access(cache_file, F_OK) == 0 )
|
if( access(cache_file, F_OK) == 0 )
|
||||||
return cache_file;
|
return cache_file;
|
||||||
@ -78,114 +51,30 @@ save_resized_album_art(void * ptr, const char * path, int srcw, int srch, int fi
|
|||||||
make_dir(dirname(cache_dir), S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
|
make_dir(dirname(cache_dir), S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
|
||||||
free(cache_dir);
|
free(cache_dir);
|
||||||
|
|
||||||
if( file )
|
if( imsrc->width > imsrc->height )
|
||||||
imsrc = gdImageCreateFromJpeg((FILE *)ptr);
|
|
||||||
else
|
|
||||||
imsrc = gdImageCreateFromJpegPtr(size, ptr);
|
|
||||||
if( !imsrc )
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
dstfile = fopen(cache_file, "w");
|
|
||||||
if( !dstfile )
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
if( srcw > srch )
|
|
||||||
{
|
{
|
||||||
dstw = 160;
|
dstw = 160;
|
||||||
dsth = (srch<<8) / ((srcw<<8)/160);
|
dsth = (imsrc->height<<8) / ((imsrc->width<<8)/160);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
dstw = (srcw<<8) / ((srch<<8)/160);
|
dstw = (imsrc->width<<8) / ((imsrc->height<<8)/160);
|
||||||
dsth = 160;
|
dsth = 160;
|
||||||
}
|
}
|
||||||
imdst = gdImageCreateTrueColor(dstw, dsth);
|
imdst = image_resize(imsrc, dstw, dsth);
|
||||||
if( !imdst )
|
if( !imdst )
|
||||||
{
|
|
||||||
gdImageDestroy(imsrc);
|
|
||||||
fclose(dstfile);
|
|
||||||
goto error;
|
goto error;
|
||||||
}
|
|
||||||
#if 0 // Try our box filter resizer instead
|
|
||||||
#ifdef __sparc__
|
|
||||||
gdImageCopyResized(imdst, imsrc, 0, 0, 0, 0, dstw, dsth, imsrc->sx, imsrc->sy);
|
|
||||||
#else
|
|
||||||
gdImageCopyResampled(imdst, imsrc, 0, 0, 0, 0, dstw, dsth, imsrc->sx, imsrc->sy);
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
boxfilter_resize(imdst, imsrc, 0, 0, 0, 0, dstw, dsth, imsrc->sx, imsrc->sy);
|
|
||||||
#endif
|
|
||||||
gdImageJpeg(imdst, dstfile, 96);
|
|
||||||
fclose(dstfile);
|
|
||||||
gdImageDestroy(imsrc);
|
|
||||||
gdImageDestroy(imdst);
|
|
||||||
|
|
||||||
return cache_file;
|
if( image_save_to_jpeg_file(imdst, cache_file) == 0 )
|
||||||
|
{
|
||||||
|
image_free(imdst);
|
||||||
|
return cache_file;
|
||||||
|
}
|
||||||
error:
|
error:
|
||||||
free(cache_file);
|
free(cache_file);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* These next few functions are to allow loading JPEG data directly from memory for libjpeg.
|
|
||||||
* The standard functions only allow you to read from a file.
|
|
||||||
* This code comes from the JpgAlleg library, at http://wiki.allegro.cc/index.php?title=Libjpeg */
|
|
||||||
struct
|
|
||||||
my_src_mgr
|
|
||||||
{
|
|
||||||
struct jpeg_source_mgr pub;
|
|
||||||
JOCTET eoi_buffer[2];
|
|
||||||
};
|
|
||||||
|
|
||||||
static void
|
|
||||||
init_source(j_decompress_ptr cinfo)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
fill_input_buffer(j_decompress_ptr cinfo)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
skip_input_data(j_decompress_ptr cinfo, long num_bytes)
|
|
||||||
{
|
|
||||||
struct my_src_mgr *src = (void *)cinfo->src;
|
|
||||||
if (num_bytes > 0)
|
|
||||||
{
|
|
||||||
while (num_bytes > (long)src->pub.bytes_in_buffer)
|
|
||||||
{
|
|
||||||
num_bytes -= (long)src->pub.bytes_in_buffer;
|
|
||||||
fill_input_buffer(cinfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
src->pub.next_input_byte += num_bytes;
|
|
||||||
src->pub.bytes_in_buffer -= num_bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
term_source(j_decompress_ptr cinfo)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
jpeg_memory_src(j_decompress_ptr cinfo, unsigned char const *buffer, size_t bufsize)
|
|
||||||
{
|
|
||||||
struct my_src_mgr *src;
|
|
||||||
if (! cinfo->src)
|
|
||||||
{
|
|
||||||
cinfo->src = (*cinfo->mem->alloc_small)((void *)cinfo, JPOOL_PERMANENT, sizeof(struct my_src_mgr));;
|
|
||||||
}
|
|
||||||
src = (void *)cinfo->src;
|
|
||||||
src->pub.init_source = init_source;
|
|
||||||
src->pub.fill_input_buffer = fill_input_buffer;
|
|
||||||
src->pub.skip_input_data = skip_input_data;
|
|
||||||
src->pub.resync_to_restart = jpeg_resync_to_restart;
|
|
||||||
src->pub.term_source = term_source;
|
|
||||||
src->pub.next_input_byte = buffer;
|
|
||||||
src->pub.bytes_in_buffer = bufsize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Simple, efficient hash function from Daniel J. Bernstein */
|
/* Simple, efficient hash function from Daniel J. Bernstein */
|
||||||
unsigned int DJBHash(const char * str, int len)
|
unsigned int DJBHash(const char * str, int len)
|
||||||
{
|
{
|
||||||
@ -204,12 +93,11 @@ unsigned int DJBHash(const char * str, int len)
|
|||||||
char *
|
char *
|
||||||
check_embedded_art(const char * path, const char * image_data, int image_size)
|
check_embedded_art(const char * path, const char * image_data, int image_size)
|
||||||
{
|
{
|
||||||
struct jpeg_decompress_struct cinfo;
|
|
||||||
struct jpeg_error_mgr jerr;
|
|
||||||
int width = 0, height = 0;
|
int width = 0, height = 0;
|
||||||
char * art_path = NULL;
|
char * art_path = NULL;
|
||||||
char * cache_dir;
|
char * cache_dir;
|
||||||
FILE * dstfile;
|
FILE * dstfile;
|
||||||
|
image * imsrc;
|
||||||
size_t nwritten;
|
size_t nwritten;
|
||||||
static char last_path[PATH_MAX];
|
static char last_path[PATH_MAX];
|
||||||
static unsigned int last_hash = 0;
|
static unsigned int last_hash = 0;
|
||||||
@ -237,22 +125,13 @@ check_embedded_art(const char * path, const char * image_data, int image_size)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cinfo.err = jpeg_std_error(&jerr);
|
imsrc = image_new_from_jpeg(NULL, 0, image_data, image_size);
|
||||||
jerr.error_exit = libjpeg_error_handler;
|
width = imsrc->width;
|
||||||
jpeg_create_decompress(&cinfo);
|
height = imsrc->height;
|
||||||
if( setjmp(setjmp_buffer) )
|
|
||||||
goto error;
|
|
||||||
jpeg_memory_src(&cinfo, (unsigned char *)image_data, image_size);
|
|
||||||
jpeg_read_header(&cinfo, TRUE);
|
|
||||||
jpeg_start_decompress(&cinfo);
|
|
||||||
width = cinfo.output_width;
|
|
||||||
height = cinfo.output_height;
|
|
||||||
error:
|
|
||||||
jpeg_destroy_decompress(&cinfo);
|
|
||||||
|
|
||||||
if( width > 160 || height > 160 )
|
if( width > 160 || height > 160 )
|
||||||
{
|
{
|
||||||
art_path = save_resized_album_art((void *)image_data, path, width, height, 0, image_size);
|
art_path = save_resized_album_art(imsrc, path);
|
||||||
}
|
}
|
||||||
else if( width > 0 && height > 0 )
|
else if( width > 0 && height > 0 )
|
||||||
{
|
{
|
||||||
@ -274,6 +153,7 @@ check_embedded_art(const char * path, const char * image_data, int image_size)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
image_free(imsrc);
|
||||||
if( !art_path )
|
if( !art_path )
|
||||||
{
|
{
|
||||||
DPRINTF(E_WARN, L_METADATA, "Invalid embedded album art in %s\n", basename((char *)path));
|
DPRINTF(E_WARN, L_METADATA, "Invalid embedded album art in %s\n", basename((char *)path));
|
||||||
@ -291,9 +171,7 @@ check_for_album_file(char * dir)
|
|||||||
{
|
{
|
||||||
char * file = malloc(PATH_MAX);
|
char * file = malloc(PATH_MAX);
|
||||||
struct album_art_name_s * album_art_name;
|
struct album_art_name_s * album_art_name;
|
||||||
struct jpeg_decompress_struct cinfo;
|
image * imsrc;
|
||||||
struct jpeg_error_mgr jerr;
|
|
||||||
static FILE * infile;
|
|
||||||
int width=0, height=0;
|
int width=0, height=0;
|
||||||
char * art_file;
|
char * art_file;
|
||||||
|
|
||||||
@ -302,28 +180,16 @@ check_for_album_file(char * dir)
|
|||||||
sprintf(file, "%s/%s", dir, album_art_name->name);
|
sprintf(file, "%s/%s", dir, album_art_name->name);
|
||||||
if( access(file, R_OK) == 0 )
|
if( access(file, R_OK) == 0 )
|
||||||
{
|
{
|
||||||
infile = fopen(file, "r");
|
imsrc = image_new_from_jpeg(file, 1, NULL, 0);
|
||||||
cinfo.err = jpeg_std_error(&jerr);
|
width = imsrc->width;
|
||||||
jerr.error_exit = libjpeg_error_handler;
|
height = imsrc->height;
|
||||||
jpeg_create_decompress(&cinfo);
|
|
||||||
if( setjmp(setjmp_buffer) )
|
|
||||||
goto error;
|
|
||||||
jpeg_stdio_src(&cinfo, infile);
|
|
||||||
jpeg_read_header(&cinfo, TRUE);
|
|
||||||
jpeg_start_decompress(&cinfo);
|
|
||||||
width = cinfo.output_width;
|
|
||||||
height = cinfo.output_height;
|
|
||||||
if( width > 160 || height > 160 )
|
if( width > 160 || height > 160 )
|
||||||
{
|
{
|
||||||
art_file = file;
|
art_file = file;
|
||||||
rewind(infile);
|
file = save_resized_album_art(imsrc, art_file);
|
||||||
file = save_resized_album_art((void *)infile, art_file, width, height, 1, 0);
|
|
||||||
free(art_file);
|
free(art_file);
|
||||||
}
|
}
|
||||||
error:
|
image_free(imsrc);
|
||||||
jpeg_destroy_decompress(&cinfo);
|
|
||||||
fclose(infile);
|
|
||||||
|
|
||||||
return(file);
|
return(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* Utility functions
|
/* Album art extraction, caching, and scaling
|
||||||
*
|
*
|
||||||
* Project : minidlna
|
* Project : minidlna
|
||||||
* Website : http://sourceforge.net/projects/minidlna/
|
* Website : http://sourceforge.net/projects/minidlna/
|
||||||
|
@ -29,9 +29,9 @@ fi
|
|||||||
|
|
||||||
${RM} ${CONFIGFILE}
|
${RM} ${CONFIGFILE}
|
||||||
|
|
||||||
echo "/* MiniUPnP Project" >> ${CONFIGFILE}
|
echo "/* MiniDLNA Project" >> ${CONFIGFILE}
|
||||||
echo " * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/" >> ${CONFIGFILE}
|
echo " * http://sourceforge.net/projects/minidlna/" >> ${CONFIGFILE}
|
||||||
echo " * (c) 2006-2008 Thomas Bernard" >> ${CONFIGFILE}
|
echo " * (c) 2008-2009 Justin Maggard" >> ${CONFIGFILE}
|
||||||
echo " * generated by $0 on `date` */" >> ${CONFIGFILE}
|
echo " * generated by $0 on `date` */" >> ${CONFIGFILE}
|
||||||
echo "#ifndef $CONFIGMACRO" >> ${CONFIGFILE}
|
echo "#ifndef $CONFIGMACRO" >> ${CONFIGFILE}
|
||||||
echo "#define $CONFIGMACRO" >> ${CONFIGFILE}
|
echo "#define $CONFIGMACRO" >> ${CONFIGFILE}
|
||||||
|
518
image_utils.c
Normal file
518
image_utils.c
Normal file
@ -0,0 +1,518 @@
|
|||||||
|
/* MiniDLNA media server
|
||||||
|
* Copyright (C) 2009 Justin Maggard
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* These functions are mostly based on code from other projects.
|
||||||
|
* There are function to effiently resize a JPEG image, and some utility functions.
|
||||||
|
* They are here to allow loading and saving JPEG data directly to or from memory with libjpeg.
|
||||||
|
* The standard functions only allow you to read from or write to a file.
|
||||||
|
*
|
||||||
|
* The reading code comes from the JpgAlleg library, at http://wiki.allegro.cc/index.php?title=Libjpeg
|
||||||
|
* The writing code was posted on a Google group from openjpeg, at http://groups.google.com/group/openjpeg/browse_thread/thread/331e6cf60f70797f
|
||||||
|
* The resize functions come from the resize_image project, at http://www.golac.fr/Image-Resizer
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <jpeglib.h>
|
||||||
|
|
||||||
|
#include "image_utils.h"
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
#define JPEG_QUALITY 96
|
||||||
|
|
||||||
|
#define COL(red, green, blue) (((red) << 24) | ((green) << 16) | ((blue) << 8) | 0xFF)
|
||||||
|
#define COL_FULL(red, green, blue, alpha) (((red) << 24) | ((green) << 16) | ((blue) << 8) | (alpha))
|
||||||
|
#define COL_RED(col) (col >> 24)
|
||||||
|
#define COL_GREEN(col) ((col >> 16) & 0xFF)
|
||||||
|
#define COL_BLUE(col) ((col >> 8) & 0xFF)
|
||||||
|
#define COL_ALPHA(col) (col & 0xFF)
|
||||||
|
#define BLACK 0x000000FF
|
||||||
|
|
||||||
|
|
||||||
|
struct my_dst_mgr {
|
||||||
|
struct jpeg_destination_mgr jdst;
|
||||||
|
JOCTET *buf;
|
||||||
|
JOCTET *off;
|
||||||
|
size_t sz;
|
||||||
|
size_t used;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Destination manager to store data in a buffer */
|
||||||
|
static void
|
||||||
|
my_dst_mgr_init(j_compress_ptr cinfo)
|
||||||
|
{
|
||||||
|
struct my_dst_mgr *dst = (void *)cinfo->dest;
|
||||||
|
|
||||||
|
dst->used = 0;
|
||||||
|
dst->sz = cinfo->image_width
|
||||||
|
* cinfo->image_height
|
||||||
|
* cinfo->input_components;
|
||||||
|
dst->buf = malloc(dst->sz * sizeof *dst->buf);
|
||||||
|
dst->off = dst->buf;
|
||||||
|
dst->jdst.next_output_byte = dst->off;
|
||||||
|
dst->jdst.free_in_buffer = dst->sz;
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean
|
||||||
|
my_dst_mgr_empty(j_compress_ptr cinfo)
|
||||||
|
{
|
||||||
|
struct my_dst_mgr *dst = (void *)cinfo->dest;
|
||||||
|
|
||||||
|
dst->sz *= 2;
|
||||||
|
dst->used = dst->off - dst->buf;
|
||||||
|
dst->buf = realloc(dst->buf, dst->sz * sizeof *dst->buf);
|
||||||
|
dst->off = dst->buf + dst->used;
|
||||||
|
dst->jdst.next_output_byte = dst->off;
|
||||||
|
dst->jdst.free_in_buffer = dst->sz - dst->used;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
my_dst_mgr_term(j_compress_ptr cinfo)
|
||||||
|
{
|
||||||
|
struct my_dst_mgr *dst = (void *)cinfo->dest;
|
||||||
|
|
||||||
|
dst->used += dst->sz - dst->jdst.free_in_buffer;
|
||||||
|
dst->off = dst->buf + dst->used;
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
jpeg_memory_dest(j_compress_ptr cinfo, struct my_dst_mgr *dst)
|
||||||
|
{
|
||||||
|
dst->jdst.init_destination = my_dst_mgr_init;
|
||||||
|
dst->jdst.empty_output_buffer = my_dst_mgr_empty;
|
||||||
|
dst->jdst.term_destination = my_dst_mgr_term;
|
||||||
|
cinfo->dest = (void *)dst;
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Source manager to read data from a buffer */
|
||||||
|
struct
|
||||||
|
my_src_mgr
|
||||||
|
{
|
||||||
|
struct jpeg_source_mgr pub;
|
||||||
|
JOCTET eoi_buffer[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
init_source(j_decompress_ptr cinfo)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
fill_input_buffer(j_decompress_ptr cinfo)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
skip_input_data(j_decompress_ptr cinfo, long num_bytes)
|
||||||
|
{
|
||||||
|
struct my_src_mgr *src = (void *)cinfo->src;
|
||||||
|
if (num_bytes > 0)
|
||||||
|
{
|
||||||
|
while (num_bytes > (long)src->pub.bytes_in_buffer)
|
||||||
|
{
|
||||||
|
num_bytes -= (long)src->pub.bytes_in_buffer;
|
||||||
|
fill_input_buffer(cinfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
src->pub.next_input_byte += num_bytes;
|
||||||
|
src->pub.bytes_in_buffer -= num_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
term_source(j_decompress_ptr cinfo)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
jpeg_memory_src(j_decompress_ptr cinfo, const unsigned char * buffer, size_t bufsize)
|
||||||
|
{
|
||||||
|
struct my_src_mgr *src;
|
||||||
|
|
||||||
|
if (! cinfo->src)
|
||||||
|
{
|
||||||
|
cinfo->src = (*cinfo->mem->alloc_small)((void *)cinfo, JPOOL_PERMANENT, sizeof(struct my_src_mgr));;
|
||||||
|
}
|
||||||
|
src = (void *)cinfo->src;
|
||||||
|
src->pub.init_source = init_source;
|
||||||
|
src->pub.fill_input_buffer = fill_input_buffer;
|
||||||
|
src->pub.skip_input_data = skip_input_data;
|
||||||
|
src->pub.resync_to_restart = jpeg_resync_to_restart;
|
||||||
|
src->pub.term_source = term_source;
|
||||||
|
src->pub.next_input_byte = buffer;
|
||||||
|
src->pub.bytes_in_buffer = bufsize;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Don't exit on error like libjpeg likes to do */
|
||||||
|
static void
|
||||||
|
libjpeg_error_handler(j_common_ptr cinfo)
|
||||||
|
{
|
||||||
|
cinfo->err->output_message(cinfo);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
image_free(image *pimage)
|
||||||
|
{
|
||||||
|
free(pimage->buf);
|
||||||
|
free(pimage);
|
||||||
|
}
|
||||||
|
|
||||||
|
pix
|
||||||
|
get_pix(image *pimage, int32_t x, int32_t y)
|
||||||
|
{
|
||||||
|
if((x >= 0) && (y >= 0) && (x < pimage->width) && (y < pimage->height))
|
||||||
|
{
|
||||||
|
return(pimage->buf[(y * pimage->width) + x]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pix vpix = BLACK;
|
||||||
|
return(vpix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
put_pix_alpha_replace(image *pimage, int32_t x, int32_t y, pix col)
|
||||||
|
{
|
||||||
|
if((x >= 0) && (y >= 0) && (x < pimage->width) && (y < pimage->height))
|
||||||
|
pimage->buf[(y * pimage->width) + x] = col;
|
||||||
|
}
|
||||||
|
|
||||||
|
image *
|
||||||
|
image_new(int32_t width, int32_t height)
|
||||||
|
{
|
||||||
|
image *vimage;
|
||||||
|
|
||||||
|
if((vimage = (image *)malloc(sizeof(image))) == NULL)
|
||||||
|
{
|
||||||
|
DPRINTF(E_WARN, L_METADATA, "malloc failed\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
vimage->width = width; vimage->height = height;
|
||||||
|
|
||||||
|
if((vimage->buf = (pix *)malloc(width * height * sizeof(pix))) == NULL)
|
||||||
|
{
|
||||||
|
DPRINTF(E_WARN, L_METADATA, "malloc failed\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return(vimage);
|
||||||
|
}
|
||||||
|
|
||||||
|
image *
|
||||||
|
image_new_from_jpeg(const char * path, int is_file, const char * buf, int size)
|
||||||
|
{
|
||||||
|
image *vimage;
|
||||||
|
FILE *file;
|
||||||
|
struct jpeg_decompress_struct cinfo;
|
||||||
|
unsigned char *line[16], *ptr;
|
||||||
|
int x, y, i, w, h, ofs;
|
||||||
|
int maxbuf;
|
||||||
|
struct jpeg_error_mgr pub;
|
||||||
|
|
||||||
|
|
||||||
|
cinfo.err = jpeg_std_error(&pub);
|
||||||
|
pub.error_exit = libjpeg_error_handler;
|
||||||
|
jpeg_create_decompress(&cinfo);
|
||||||
|
if( is_file )
|
||||||
|
{
|
||||||
|
if( (file = fopen(path, "r")) == NULL )
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
jpeg_stdio_src(&cinfo, file);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
jpeg_memory_src(&cinfo, (const unsigned char *)buf, size);
|
||||||
|
}
|
||||||
|
jpeg_read_header(&cinfo, TRUE);
|
||||||
|
cinfo.do_fancy_upsampling = FALSE;
|
||||||
|
cinfo.do_block_smoothing = FALSE;
|
||||||
|
jpeg_start_decompress(&cinfo);
|
||||||
|
w = cinfo.output_width;
|
||||||
|
h = cinfo.output_height;
|
||||||
|
vimage = image_new(w, h);
|
||||||
|
if(!vimage)
|
||||||
|
{
|
||||||
|
jpeg_destroy_decompress(&cinfo);
|
||||||
|
if( is_file )
|
||||||
|
fclose(file);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cinfo.rec_outbuf_height > 16)
|
||||||
|
{
|
||||||
|
DPRINTF(E_WARN, L_METADATA, "ERROR image_from_jpeg : (image_from_jpeg.c) JPEG uses line buffers > 16. Cannot load.\n");
|
||||||
|
image_free(vimage);
|
||||||
|
if( is_file )
|
||||||
|
fclose(file);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
maxbuf = vimage->width * vimage->height;
|
||||||
|
if(cinfo.output_components == 3)
|
||||||
|
{
|
||||||
|
ofs = 0;
|
||||||
|
if((ptr = (unsigned char *)malloc(w * 3 * cinfo.rec_outbuf_height)) == NULL)
|
||||||
|
{
|
||||||
|
DPRINTF(E_WARN, L_METADATA, "malloc failed\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(y = 0; y < h; y += cinfo.rec_outbuf_height)
|
||||||
|
{
|
||||||
|
for(i = 0; i < cinfo.rec_outbuf_height; i++)
|
||||||
|
{
|
||||||
|
line[i] = ptr + (w * 3 * i);
|
||||||
|
}
|
||||||
|
jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height);
|
||||||
|
for(x = 0; x < w * cinfo.rec_outbuf_height; x++)
|
||||||
|
{
|
||||||
|
if( ofs < maxbuf )
|
||||||
|
{
|
||||||
|
vimage->buf[ofs] = COL(ptr[x + x + x], ptr[x + x + x + 1], ptr[x + x + x + 2]);
|
||||||
|
ofs++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
else if(cinfo.output_components == 1)
|
||||||
|
{
|
||||||
|
ofs = 0;
|
||||||
|
for(i = 0; i < cinfo.rec_outbuf_height; i++)
|
||||||
|
{
|
||||||
|
if((line[i] = (unsigned char *)malloc(w)) == NULL)
|
||||||
|
{
|
||||||
|
int t = 0;
|
||||||
|
|
||||||
|
for(t = 0; t < i; t++) free(line[t]);
|
||||||
|
jpeg_destroy_decompress(&cinfo);
|
||||||
|
image_free(vimage);
|
||||||
|
if( is_file )
|
||||||
|
fclose(file);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(y = 0; y < h; y += cinfo.rec_outbuf_height)
|
||||||
|
{
|
||||||
|
jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height);
|
||||||
|
for(i = 0; i < cinfo.rec_outbuf_height; i++)
|
||||||
|
{
|
||||||
|
for(x = 0; x < w; x++)
|
||||||
|
{
|
||||||
|
vimage->buf[ofs++] = COL(line[i][x], line[i][x], line[i][x]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(i = 0; i < cinfo.rec_outbuf_height; i++)
|
||||||
|
{
|
||||||
|
free(line[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jpeg_finish_decompress(&cinfo);
|
||||||
|
jpeg_destroy_decompress(&cinfo);
|
||||||
|
if( is_file )
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
return vimage;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
image_resize_nearest(image * pdest, image * psrc, int32_t width, int32_t height)
|
||||||
|
{
|
||||||
|
int32_t vx, vy, rx, ry;
|
||||||
|
pix vcol;
|
||||||
|
|
||||||
|
if((pdest == NULL) || (psrc == NULL))
|
||||||
|
return;
|
||||||
|
|
||||||
|
for(vy = 0; vy < height; vy++)
|
||||||
|
{
|
||||||
|
for(vx = 0; vx < width; vx++)
|
||||||
|
{
|
||||||
|
rx = ((vx * psrc->width) / width);
|
||||||
|
ry = ((vy * psrc->height) / height);
|
||||||
|
vcol = get_pix(psrc, rx, ry);
|
||||||
|
put_pix_alpha_replace(pdest, vx, vy, vcol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
image_downsize_rought(image * pdest, image * psrc, int32_t width, int32_t height)
|
||||||
|
{
|
||||||
|
int32_t vx, vy;
|
||||||
|
pix vcol;
|
||||||
|
int32_t i, j;
|
||||||
|
int32_t rx, ry, rx_next, ry_next;
|
||||||
|
int red, green, blue, alpha;
|
||||||
|
int factor;
|
||||||
|
|
||||||
|
if((pdest == NULL) || (psrc == NULL))
|
||||||
|
return;
|
||||||
|
|
||||||
|
for(vy = 0; vy < height; vy++)
|
||||||
|
{
|
||||||
|
for(vx = 0; vx < width; vx++)
|
||||||
|
{
|
||||||
|
|
||||||
|
rx = ((vx * psrc->width) / width);
|
||||||
|
ry = ((vy * psrc->height) / height);
|
||||||
|
|
||||||
|
red = green = blue = alpha = 0;
|
||||||
|
|
||||||
|
rx_next = rx + (psrc->width / width);
|
||||||
|
ry_next = ry + (psrc->width / width);
|
||||||
|
factor = 0;
|
||||||
|
|
||||||
|
for( j = rx; j < rx_next; j++)
|
||||||
|
{
|
||||||
|
for( i = ry; i < ry_next; i++)
|
||||||
|
{
|
||||||
|
factor += 1;
|
||||||
|
vcol = get_pix(psrc, j, i);
|
||||||
|
|
||||||
|
red += COL_RED(vcol);
|
||||||
|
green += COL_GREEN(vcol);
|
||||||
|
blue += COL_BLUE(vcol);
|
||||||
|
alpha += COL_ALPHA(vcol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
red /= factor;
|
||||||
|
green /= factor;
|
||||||
|
blue /= factor;
|
||||||
|
alpha /= factor;
|
||||||
|
|
||||||
|
/* on sature les valeurs */
|
||||||
|
red = (red > 255) ? 255 : ((red < 0) ? 0 : red );
|
||||||
|
green = (green > 255) ? 255 : ((green < 0) ? 0 : green);
|
||||||
|
blue = (blue > 255) ? 255 : ((blue < 0) ? 0 : blue );
|
||||||
|
alpha = (alpha > 255) ? 255 : ((alpha < 0) ? 0 : alpha);
|
||||||
|
|
||||||
|
put_pix_alpha_replace(pdest, vx, vy,
|
||||||
|
COL_FULL((u_int8_t)red, (u_int8_t)green, (u_int8_t)blue, (u_int8_t)alpha));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
image *
|
||||||
|
image_resize(image * src_image, int32_t width, int32_t height)
|
||||||
|
{
|
||||||
|
image * dst_image;
|
||||||
|
|
||||||
|
dst_image = image_new(width, height);
|
||||||
|
if( !dst_image )
|
||||||
|
return NULL;
|
||||||
|
if( (src_image->width < width) || (src_image->height < height) )
|
||||||
|
image_resize_nearest(dst_image, src_image, width, height);
|
||||||
|
else
|
||||||
|
image_downsize_rought(dst_image, src_image, width, height);
|
||||||
|
|
||||||
|
return dst_image;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
unsigned char *
|
||||||
|
image_save_to_jpeg_buf(image * pimage, int * size)
|
||||||
|
{
|
||||||
|
struct jpeg_compress_struct cinfo;
|
||||||
|
struct jpeg_error_mgr jerr;
|
||||||
|
JSAMPROW row_pointer[1];
|
||||||
|
int row_stride;
|
||||||
|
char *data;
|
||||||
|
int i, x;
|
||||||
|
struct my_dst_mgr dst;
|
||||||
|
|
||||||
|
cinfo.err = jpeg_std_error(&jerr);
|
||||||
|
jpeg_create_compress(&cinfo);
|
||||||
|
jpeg_memory_dest(&cinfo, &dst);
|
||||||
|
cinfo.image_width = pimage->width;
|
||||||
|
cinfo.image_height = pimage->height;
|
||||||
|
cinfo.input_components = 3;
|
||||||
|
cinfo.in_color_space = JCS_RGB;
|
||||||
|
jpeg_set_defaults(&cinfo);
|
||||||
|
jpeg_set_quality(&cinfo, JPEG_QUALITY, TRUE);
|
||||||
|
jpeg_start_compress(&cinfo, TRUE);
|
||||||
|
row_stride = cinfo.image_width * 3;
|
||||||
|
if((data = malloc(row_stride)) == NULL)
|
||||||
|
{
|
||||||
|
DPRINTF(E_WARN, L_METADATA, "malloc failed\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
i = 0;
|
||||||
|
while(cinfo.next_scanline < cinfo.image_height)
|
||||||
|
{
|
||||||
|
for(x = 0; x < pimage->width; x++)
|
||||||
|
{
|
||||||
|
data[x + x + x] = COL_RED(pimage->buf[i]);
|
||||||
|
data[x + x + x + 1] = COL_GREEN(pimage->buf[i]);
|
||||||
|
data[x + x + x + 2] = COL_BLUE(pimage->buf[i]);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
row_pointer[0] = (unsigned char *)data;
|
||||||
|
jpeg_write_scanlines(&cinfo, row_pointer, 1);
|
||||||
|
}
|
||||||
|
jpeg_finish_compress(&cinfo);
|
||||||
|
*size = dst.used;
|
||||||
|
free(data);
|
||||||
|
jpeg_destroy_compress(&cinfo);
|
||||||
|
|
||||||
|
return dst.buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
image_save_to_jpeg_file(image * pimage, const char * path)
|
||||||
|
{
|
||||||
|
int nwritten, size = 0;
|
||||||
|
unsigned char * buf;
|
||||||
|
FILE * dst_file;
|
||||||
|
|
||||||
|
buf = image_save_to_jpeg_buf(pimage, &size);
|
||||||
|
if( !buf )
|
||||||
|
return -1;
|
||||||
|
dst_file = fopen(path, "w");
|
||||||
|
if( !dst_file )
|
||||||
|
{
|
||||||
|
free(buf);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
nwritten = fwrite(buf, 1, size, dst_file);
|
||||||
|
fclose(dst_file);
|
||||||
|
free(buf);
|
||||||
|
|
||||||
|
return (nwritten==size ? 0 : 1);
|
||||||
|
}
|
33
image_utils.h
Normal file
33
image_utils.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/* Image manipulation functions
|
||||||
|
*
|
||||||
|
* Project : minidlna
|
||||||
|
* Website : http://sourceforge.net/projects/minidlna/
|
||||||
|
* Author : Justin Maggard
|
||||||
|
* Copyright (c) 2009 Justin Maggard
|
||||||
|
* This software is subject to the conditions detailed in the
|
||||||
|
* LICENCE file provided in this distribution.
|
||||||
|
* */
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
typedef u_int32_t pix;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int32_t width;
|
||||||
|
int32_t height;
|
||||||
|
pix *buf;
|
||||||
|
} image;
|
||||||
|
|
||||||
|
void
|
||||||
|
image_free(image *pimage);
|
||||||
|
|
||||||
|
image *
|
||||||
|
image_new_from_jpeg(const char * path, int is_file, const char * ptr, int size);
|
||||||
|
|
||||||
|
image *
|
||||||
|
image_resize(image * src_image, int32_t width, int32_t height);
|
||||||
|
|
||||||
|
unsigned char *
|
||||||
|
image_save_to_jpeg_buf(image * pimage, int * size);
|
||||||
|
|
||||||
|
int
|
||||||
|
image_save_to_jpeg_file(image * pimage, const char * path);
|
@ -1,5 +1,5 @@
|
|||||||
/* MiniDLNA media server
|
/* MiniDLNA media server
|
||||||
* Copyright (C) 2008 Justin Maggard
|
* Copyright (C) 2008-2009 Justin Maggard
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
* Project : minidlna
|
* Project : minidlna
|
||||||
* Website : http://sourceforge.net/projects/minidlna/
|
* Website : http://sourceforge.net/projects/minidlna/
|
||||||
* Author : Justin Maggard
|
* Author : Justin Maggard
|
||||||
* Copyright (c) 2008 Justin Maggard
|
* Copyright (c) 2008-2009 Justin Maggard
|
||||||
* This software is subject to the conditions detailed in the
|
* This software is subject to the conditions detailed in the
|
||||||
* LICENCE file provided in this distribution.
|
* LICENCE file provided in this distribution.
|
||||||
* */
|
* */
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/* MiniDLNA project
|
/* MiniDLNA project
|
||||||
*
|
*
|
||||||
* http://sourceforge.net/projects/minidlna/
|
* http://sourceforge.net/projects/minidlna/
|
||||||
* (c) 2008 Justin Maggard
|
* (c) 2008-2009 Justin Maggard
|
||||||
* This software is subject to the conditions detailed
|
* This software is subject to the conditions detailed
|
||||||
* in the LICENCE file provided within the distribution
|
* in the LICENCE file provided within the distribution
|
||||||
*
|
*
|
||||||
@ -594,9 +594,11 @@ main(int argc, char * * argv)
|
|||||||
struct timeval timeout, timeofday, lasttimeofday = {0, 0}, lastupdatetime = {0, 0};
|
struct timeval timeout, timeofday, lasttimeofday = {0, 0}, lastupdatetime = {0, 0};
|
||||||
int max_fd = -1;
|
int max_fd = -1;
|
||||||
int last_changecnt = 0;
|
int last_changecnt = 0;
|
||||||
|
#ifdef TIVO_SUPPORT
|
||||||
unsigned short int loop_cnt = 0;
|
unsigned short int loop_cnt = 0;
|
||||||
int sbeacon;
|
int sbeacon;
|
||||||
struct sockaddr_in tivo_bcast;
|
struct sockaddr_in tivo_bcast;
|
||||||
|
#endif
|
||||||
char * sql;
|
char * sql;
|
||||||
pthread_t thread[2];
|
pthread_t thread[2];
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/* MiniDLNA media server
|
/* MiniDLNA media server
|
||||||
* Copyright (C) 2008 Justin Maggard
|
* Copyright (C) 2008-2009 Justin Maggard
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
* Project : minidlna
|
* Project : minidlna
|
||||||
* Website : http://sourceforge.net/projects/minidlna/
|
* Website : http://sourceforge.net/projects/minidlna/
|
||||||
* Author : Justin Maggard
|
* Author : Justin Maggard
|
||||||
* Copyright (c) 2008 Justin Maggard
|
* Copyright (c) 2008-2009 Justin Maggard
|
||||||
* This software is subject to the conditions detailed in the
|
* This software is subject to the conditions detailed in the
|
||||||
* LICENCE file provided in this distribution.
|
* LICENCE file provided in this distribution.
|
||||||
* */
|
* */
|
||||||
|
2
sql.c
2
sql.c
@ -1,5 +1,5 @@
|
|||||||
/* MiniDLNA media server
|
/* MiniDLNA media server
|
||||||
* Copyright (C) 2008 Justin Maggard
|
* Copyright (C) 2008-2009 Justin Maggard
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
2
sql.h
2
sql.h
@ -3,7 +3,7 @@
|
|||||||
* Project : minidlna
|
* Project : minidlna
|
||||||
* Website : http://sourceforge.net/projects/minidlna/
|
* Website : http://sourceforge.net/projects/minidlna/
|
||||||
* Author : Justin Maggard
|
* Author : Justin Maggard
|
||||||
* Copyright (c) 2008 Justin Maggard
|
* Copyright (c) 2008-2009 Justin Maggard
|
||||||
* This software is subject to the conditions detailed in the
|
* This software is subject to the conditions detailed in the
|
||||||
* LICENCE file provided in this distribution.
|
* LICENCE file provided in this distribution.
|
||||||
* */
|
* */
|
||||||
|
@ -1,3 +1,12 @@
|
|||||||
|
/* TiVo discovery
|
||||||
|
*
|
||||||
|
* Project : minidlna
|
||||||
|
* Website : http://sourceforge.net/projects/minidlna/
|
||||||
|
* Author : Justin Maggard
|
||||||
|
* Copyright (c) 2009 Justin Maggard
|
||||||
|
* This software is subject to the conditions detailed in the
|
||||||
|
* LICENCE file provided in this distribution.
|
||||||
|
* */
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#ifdef TIVO_SUPPORT
|
#ifdef TIVO_SUPPORT
|
||||||
/*
|
/*
|
||||||
|
@ -1,3 +1,20 @@
|
|||||||
|
/* MiniDLNA media server
|
||||||
|
* Copyright (C) 2009 Justin Maggard
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#ifdef TIVO_SUPPORT
|
#ifdef TIVO_SUPPORT
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@ -56,6 +73,7 @@ SendRootContainer(struct upnphttp * h)
|
|||||||
"</Item>"
|
"</Item>"
|
||||||
"</TiVoContainer>", friendly_name, friendly_name, friendly_name);
|
"</TiVoContainer>", friendly_name, friendly_name, friendly_name);
|
||||||
BuildResp_upnphttp(h, resp, len);
|
BuildResp_upnphttp(h, resp, len);
|
||||||
|
free(resp);
|
||||||
SendResp_upnphttp(h);
|
SendResp_upnphttp(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,16 @@
|
|||||||
|
/* TiVo command processing
|
||||||
|
*
|
||||||
|
* Project : minidlna
|
||||||
|
* Website : http://sourceforge.net/projects/minidlna/
|
||||||
|
* Author : Justin Maggard
|
||||||
|
* Copyright (c) 2009 Justin Maggard
|
||||||
|
* This software is subject to the conditions detailed in the
|
||||||
|
* LICENCE file provided in this distribution.
|
||||||
|
* */
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#ifdef TIVO_SUPPORT
|
#ifdef TIVO_SUPPORT
|
||||||
|
|
||||||
void
|
void
|
||||||
ProcessTiVoCommand(struct upnphttp * h, const char * orig_path);
|
ProcessTiVoCommand(struct upnphttp * h, const char * orig_path);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
17
tivo_utils.c
17
tivo_utils.c
@ -1,3 +1,20 @@
|
|||||||
|
/* MiniDLNA media server
|
||||||
|
* Copyright (C) 2009 Justin Maggard
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#ifdef TIVO_SUPPORT
|
#ifdef TIVO_SUPPORT
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
10
tivo_utils.h
10
tivo_utils.h
@ -1,3 +1,12 @@
|
|||||||
|
/* TiVo helper functions
|
||||||
|
*
|
||||||
|
* Project : minidlna
|
||||||
|
* Website : http://sourceforge.net/projects/minidlna/
|
||||||
|
* Author : Justin Maggard
|
||||||
|
* Copyright (c) 2009 Justin Maggard
|
||||||
|
* This software is subject to the conditions detailed in the
|
||||||
|
* LICENCE file provided in this distribution.
|
||||||
|
* */
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#ifdef TIVO_SUPPORT
|
#ifdef TIVO_SUPPORT
|
||||||
#include <sqlite3.h>
|
#include <sqlite3.h>
|
||||||
@ -13,4 +22,5 @@ decodeString(char * string, int inplace);
|
|||||||
|
|
||||||
void
|
void
|
||||||
TiVoRandomSeedFunc(sqlite3_context *context, int argc, sqlite3_value **argv);
|
TiVoRandomSeedFunc(sqlite3_context *context, int argc, sqlite3_value **argv);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -57,9 +57,9 @@ extern time_t startup_time;
|
|||||||
extern struct runtime_vars_s runtime_vars;
|
extern struct runtime_vars_s runtime_vars;
|
||||||
/* runtime boolean flags */
|
/* runtime boolean flags */
|
||||||
extern int runtime_flags;
|
extern int runtime_flags;
|
||||||
#define INOTIFYMASK 0x0001
|
#define INOTIFYMASK 0x0001
|
||||||
#define SYSUPTIMEMASK 0x0002
|
#define SYSUPTIMEMASK 0x0002
|
||||||
#define TIVOMASK 0x0004
|
#define TIVOMASK 0x0004
|
||||||
|
|
||||||
#define SETFLAG(mask) runtime_flags |= mask
|
#define SETFLAG(mask) runtime_flags |= mask
|
||||||
#define GETFLAG(mask) runtime_flags & mask
|
#define GETFLAG(mask) runtime_flags & mask
|
||||||
|
50
upnphttp.c
50
upnphttp.c
@ -1,6 +1,6 @@
|
|||||||
/* MiniDLNA project
|
/* MiniDLNA project
|
||||||
* http://minidlna.sourceforge.net/
|
* http://minidlna.sourceforge.net/
|
||||||
* (c) 2008 Justin Maggard
|
* (c) 2008-2009 Justin Maggard
|
||||||
*
|
*
|
||||||
* This software is subject to the conditions detailed
|
* This software is subject to the conditions detailed
|
||||||
* in the LICENCE file provided within the distribution
|
* in the LICENCE file provided within the distribution
|
||||||
@ -33,10 +33,10 @@
|
|||||||
|
|
||||||
#include "upnpglobalvars.h"
|
#include "upnpglobalvars.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
#include "image_utils.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "sql.h"
|
#include "sql.h"
|
||||||
#include <libexif/exif-loader.h>
|
#include <libexif/exif-loader.h>
|
||||||
#include <gd.h>
|
|
||||||
#ifdef TIVO_SUPPORT
|
#ifdef TIVO_SUPPORT
|
||||||
#include "tivo_utils.h"
|
#include "tivo_utils.h"
|
||||||
#include "tivo_commands.h"
|
#include "tivo_commands.h"
|
||||||
@ -723,7 +723,6 @@ static const char httpresphead[] =
|
|||||||
"Content-Length: %d\r\n"
|
"Content-Length: %d\r\n"
|
||||||
/*"Server: miniupnpd/1.0 UPnP/1.0\r\n"*/
|
/*"Server: miniupnpd/1.0 UPnP/1.0\r\n"*/
|
||||||
// "Accept-Ranges: bytes\r\n"
|
// "Accept-Ranges: bytes\r\n"
|
||||||
// "DATE: Wed, 24 Sep 2008 05:57:19 GMT\r\n"
|
|
||||||
//"Server: " MINIUPNPD_SERVER_STRING "\r\n"
|
//"Server: " MINIUPNPD_SERVER_STRING "\r\n"
|
||||||
; /*"\r\n";*/
|
; /*"\r\n";*/
|
||||||
/*
|
/*
|
||||||
@ -1114,18 +1113,18 @@ SendResp_resizedimg(struct upnphttp * h, char * object)
|
|||||||
char **result;
|
char **result;
|
||||||
char date[30];
|
char date[30];
|
||||||
time_t curtime = time(NULL);
|
time_t curtime = time(NULL);
|
||||||
FILE *imgfile;
|
|
||||||
int width=640, height=480, dstw, dsth, rotation, size;
|
int width=640, height=480, dstw, dsth, rotation, size;
|
||||||
char * data;
|
unsigned char * data;
|
||||||
char *path, *file_path;
|
char *path, *file_path;
|
||||||
char *resolution, *tn;
|
char *resolution, *tn;
|
||||||
char *key, *val;
|
char *key, *val;
|
||||||
char *saveptr, *item = NULL;
|
char *saveptr, *item = NULL;
|
||||||
char *pixelshape = NULL;
|
char *pixelshape = NULL;
|
||||||
int rows=0, ret;
|
int rows=0, ret;
|
||||||
gdImagePtr imsrc = 0, imdst = 0;
|
|
||||||
ExifData *ed;
|
ExifData *ed;
|
||||||
ExifLoader *l;
|
ExifLoader *l;
|
||||||
|
image * imsrc;
|
||||||
|
image * imdst;
|
||||||
#if USE_FORK
|
#if USE_FORK
|
||||||
pid_t newpid = 0;
|
pid_t newpid = 0;
|
||||||
newpid = fork();
|
newpid = fork();
|
||||||
@ -1178,8 +1177,8 @@ SendResp_resizedimg(struct upnphttp * h, char * object)
|
|||||||
ret = sql_get_table(db, sql_buf, &result, &rows, NULL);
|
ret = sql_get_table(db, sql_buf, &result, &rows, NULL);
|
||||||
if( (ret != SQLITE_OK) || !rows || (access(result[3], F_OK) != 0) )
|
if( (ret != SQLITE_OK) || !rows || (access(result[3], F_OK) != 0) )
|
||||||
{
|
{
|
||||||
free(path);
|
|
||||||
DPRINTF(E_ERROR, L_HTTP, "Didn't find valid file for %s!\n", path);
|
DPRINTF(E_ERROR, L_HTTP, "Didn't find valid file for %s!\n", path);
|
||||||
|
free(path);
|
||||||
goto resized_error;
|
goto resized_error;
|
||||||
}
|
}
|
||||||
file_path = result[3];
|
file_path = result[3];
|
||||||
@ -1204,20 +1203,12 @@ SendResp_resizedimg(struct upnphttp * h, char * object)
|
|||||||
sqlite3_free_table(result);
|
sqlite3_free_table(result);
|
||||||
goto resized_error;
|
goto resized_error;
|
||||||
}
|
}
|
||||||
imsrc = gdImageCreateFromJpegPtr(ed->size, ed->data);
|
imsrc = image_new_from_jpeg(NULL, 0, (char *)ed->data, ed->size);
|
||||||
exif_data_unref(ed);
|
exif_data_unref(ed);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
imgfile = fopen(file_path, "r");
|
imsrc = image_new_from_jpeg(file_path, 1, NULL, 0);
|
||||||
if( !imgfile )
|
|
||||||
{
|
|
||||||
Send404(h);
|
|
||||||
sqlite3_free_table(result);
|
|
||||||
goto resized_error;
|
|
||||||
}
|
|
||||||
imsrc = gdImageCreateFromJpeg(imgfile);
|
|
||||||
fclose(imgfile);
|
|
||||||
}
|
}
|
||||||
if( !imsrc )
|
if( !imsrc )
|
||||||
{
|
{
|
||||||
@ -1227,23 +1218,15 @@ SendResp_resizedimg(struct upnphttp * h, char * object)
|
|||||||
}
|
}
|
||||||
/* Figure out the best destination resolution we can use */
|
/* Figure out the best destination resolution we can use */
|
||||||
dstw = width;
|
dstw = width;
|
||||||
dsth = ((((width<<10)/imsrc->sx)*imsrc->sy)>>10);
|
dsth = ((((width<<10)/imsrc->width)*imsrc->height)>>10);
|
||||||
if( dsth > height )
|
if( dsth > height )
|
||||||
{
|
{
|
||||||
dsth = height;
|
dsth = height;
|
||||||
dstw = (((height<<10)/imsrc->sy) * imsrc->sx>>10);
|
dstw = (((height<<10)/imsrc->height) * imsrc->width>>10);
|
||||||
}
|
}
|
||||||
imdst = gdImageCreateTrueColor(dstw, dsth);
|
imdst = image_resize(imsrc, dstw, dsth);
|
||||||
#if 0 // Use box filter resizer
|
data = image_save_to_jpeg_buf(imdst, &size);
|
||||||
#ifdef __sparc__
|
|
||||||
gdImageCopyResized(imdst, imsrc, 0, 0, 0, 0, dstw, dsth, imsrc->sx, imsrc->sy);
|
|
||||||
#else
|
|
||||||
gdImageCopyResampled(imdst, imsrc, 0, 0, 0, 0, dstw, dsth, imsrc->sx, imsrc->sy);
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
boxfilter_resize(imdst, imsrc, 0, 0, 0, 0, dstw, dsth, imsrc->sx, imsrc->sy);
|
|
||||||
#endif
|
|
||||||
data = (char *)gdImageJpegPtr(imdst, &size, 99);
|
|
||||||
DPRINTF(E_INFO, L_HTTP, "size: %d\n", size);
|
DPRINTF(E_INFO, L_HTTP, "size: %d\n", size);
|
||||||
strftime(date, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&curtime));
|
strftime(date, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&curtime));
|
||||||
snprintf(header, sizeof(header)-50, "%s 200 OK\r\n"
|
snprintf(header, sizeof(header)-50, "%s 200 OK\r\n"
|
||||||
@ -1268,12 +1251,11 @@ SendResp_resizedimg(struct upnphttp * h, char * object)
|
|||||||
|
|
||||||
if( (send_data(h, header, strlen(header)) == 0) && (h->req_command != EHead) )
|
if( (send_data(h, header, strlen(header)) == 0) && (h->req_command != EHead) )
|
||||||
{
|
{
|
||||||
send_data(h, data, size);
|
send_data(h, (char *)data, size);
|
||||||
}
|
}
|
||||||
gdFree(data);
|
|
||||||
gdImageDestroy(imsrc);
|
|
||||||
gdImageDestroy(imdst);
|
|
||||||
DPRINTF(E_INFO, L_HTTP, "Done serving %s\n", file_path);
|
DPRINTF(E_INFO, L_HTTP, "Done serving %s\n", file_path);
|
||||||
|
image_free(imsrc);
|
||||||
|
image_free(imdst);
|
||||||
sqlite3_free_table(result);
|
sqlite3_free_table(result);
|
||||||
resized_error:
|
resized_error:
|
||||||
#if USE_FORK
|
#if USE_FORK
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* MiniDLNA project
|
/* MiniDLNA project
|
||||||
* http://minidlna.sourceforge.net/
|
* http://minidlna.sourceforge.net/
|
||||||
* (c) 2008 Justin Maggard
|
* (c) 2008-2009 Justin Maggard
|
||||||
*
|
*
|
||||||
* This software is subject to the conditions detailed
|
* This software is subject to the conditions detailed
|
||||||
* in the LICENCE file provided within the distribution
|
* in the LICENCE file provided within the distribution
|
||||||
|
128
utils.c
128
utils.c
@ -1,5 +1,5 @@
|
|||||||
/* MiniDLNA media server
|
/* MiniDLNA media server
|
||||||
* Copyright (C) 2008 Justin Maggard
|
* Copyright (C) 2008-2009 Justin Maggard
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -25,7 +25,6 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <gd.h>
|
|
||||||
|
|
||||||
int
|
int
|
||||||
ends_with(const char * haystack, const char * needle)
|
ends_with(const char * haystack, const char * needle)
|
||||||
@ -148,128 +147,3 @@ make_dir(char * path, mode_t mode)
|
|||||||
printf("make_dir: cannot create directory '%s'", path);
|
printf("make_dir: cannot create directory '%s'", path);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Use our own boxfilter resizer, because gdCopyImageResampled is slow,
|
|
||||||
* and gdCopyImageResized looks horrible when you downscale much. */
|
|
||||||
#define N_FRAC 8
|
|
||||||
#define MASK_FRAC ((1 << N_FRAC) - 1)
|
|
||||||
#define ROUND2(v) (((v) + (1 << (N_FRAC - 1))) >> N_FRAC)
|
|
||||||
#define DIV(x, y) ( ((x) << (N_FRAC - 3)) / ((y) >> 3) )
|
|
||||||
void
|
|
||||||
boxfilter_resize(gdImagePtr dst, gdImagePtr src,
|
|
||||||
int dstX, int dstY, int srcX, int srcY,
|
|
||||||
int dstW, int dstH, int srcW, int srcH)
|
|
||||||
{
|
|
||||||
int x, y;
|
|
||||||
int sy1, sy2, sx1, sx2;
|
|
||||||
|
|
||||||
if(!dst->trueColor)
|
|
||||||
{
|
|
||||||
gdImageCopyResized(dst, src, dstX, dstY, srcX, srcY, dstW, dstH,
|
|
||||||
srcW, srcH);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for(y = dstY; y < (dstY + dstH); y++)
|
|
||||||
{
|
|
||||||
sy1 = (((y - dstY) * srcH) << N_FRAC) / dstH;
|
|
||||||
sy2 = (((y - dstY + 1) * srcH) << N_FRAC) / dstH;
|
|
||||||
for(x = dstX; x < (dstX + dstW); x++)
|
|
||||||
{
|
|
||||||
int sx, sy;
|
|
||||||
int spixels = 0;
|
|
||||||
int red = 0, green = 0, blue = 0, alpha = 0;
|
|
||||||
sx1 = (((x - dstX) * srcW) << N_FRAC) / dstW;
|
|
||||||
sx2 = (((x - dstX + 1) * srcW) << N_FRAC) / dstW;
|
|
||||||
sy = sy1;
|
|
||||||
do {
|
|
||||||
int yportion;
|
|
||||||
if((sy >> N_FRAC) == (sy1 >> N_FRAC))
|
|
||||||
{
|
|
||||||
yportion = (1 << N_FRAC) - (sy & MASK_FRAC);
|
|
||||||
if(yportion > sy2 - sy1)
|
|
||||||
{
|
|
||||||
yportion = sy2 - sy1;
|
|
||||||
}
|
|
||||||
sy = sy & ~MASK_FRAC;
|
|
||||||
}
|
|
||||||
else if(sy == (sy2 & ~MASK_FRAC))
|
|
||||||
{
|
|
||||||
yportion = sy2 & MASK_FRAC;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
yportion = (1 << N_FRAC);
|
|
||||||
}
|
|
||||||
sx = sx1;
|
|
||||||
do {
|
|
||||||
int xportion;
|
|
||||||
int pcontribution;
|
|
||||||
int p;
|
|
||||||
if((sx >> N_FRAC) == (sx1 >> N_FRAC))
|
|
||||||
{
|
|
||||||
xportion = (1 << N_FRAC) - (sx & MASK_FRAC);
|
|
||||||
if(xportion > sx2 - sx1)
|
|
||||||
{
|
|
||||||
xportion = sx2 - sx1;
|
|
||||||
}
|
|
||||||
sx = sx & ~MASK_FRAC;
|
|
||||||
}
|
|
||||||
else if(sx == (sx2 & ~MASK_FRAC))
|
|
||||||
{
|
|
||||||
xportion = sx2 & MASK_FRAC;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
xportion = (1 << N_FRAC);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(xportion && yportion)
|
|
||||||
{
|
|
||||||
pcontribution = (xportion * yportion) >> N_FRAC;
|
|
||||||
p = gdImageGetTrueColorPixel(src, ROUND2(sx) + srcX, ROUND2(sy) + srcY);
|
|
||||||
if(pcontribution == (1 << N_FRAC))
|
|
||||||
{
|
|
||||||
// optimization for down-scaler, which many pixel has pcontribution=1
|
|
||||||
red += gdTrueColorGetRed(p) << N_FRAC;
|
|
||||||
green += gdTrueColorGetGreen(p) << N_FRAC;
|
|
||||||
blue += gdTrueColorGetBlue(p) << N_FRAC;
|
|
||||||
alpha += gdTrueColorGetAlpha(p) << N_FRAC;
|
|
||||||
spixels += (1 << N_FRAC);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
red += gdTrueColorGetRed(p) * pcontribution;
|
|
||||||
green += gdTrueColorGetGreen(p) * pcontribution;
|
|
||||||
blue += gdTrueColorGetBlue(p) * pcontribution;
|
|
||||||
alpha += gdTrueColorGetAlpha(p) * pcontribution;
|
|
||||||
spixels += pcontribution;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sx += (1 << N_FRAC);
|
|
||||||
}
|
|
||||||
while(sx < sx2);
|
|
||||||
sy += (1 << N_FRAC);
|
|
||||||
}
|
|
||||||
while(sy < sy2);
|
|
||||||
if(spixels != 0)
|
|
||||||
{
|
|
||||||
red = DIV(red, spixels);
|
|
||||||
green = DIV(green, spixels);
|
|
||||||
blue = DIV(blue, spixels);
|
|
||||||
alpha = DIV(alpha, spixels);
|
|
||||||
}
|
|
||||||
/* Clamping to allow for rounding errors above */
|
|
||||||
if(red > (255 << N_FRAC))
|
|
||||||
red = (255 << N_FRAC);
|
|
||||||
if(green > (255 << N_FRAC))
|
|
||||||
green = (255 << N_FRAC);
|
|
||||||
if(blue > (255 << N_FRAC))
|
|
||||||
blue = (255 << N_FRAC);
|
|
||||||
if(alpha > (gdAlphaMax << N_FRAC))
|
|
||||||
alpha = (gdAlphaMax << N_FRAC);
|
|
||||||
gdImageSetPixel(dst, x, y,
|
|
||||||
gdTrueColorAlpha(ROUND2(red), ROUND2(green), ROUND2(blue), ROUND2(alpha)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
8
utils.h
8
utils.h
@ -3,13 +3,12 @@
|
|||||||
* Project : minidlna
|
* Project : minidlna
|
||||||
* Website : http://sourceforge.net/projects/minidlna/
|
* Website : http://sourceforge.net/projects/minidlna/
|
||||||
* Author : Justin Maggard
|
* Author : Justin Maggard
|
||||||
* Copyright (c) 2008 Justin Maggard
|
* Copyright (c) 2008-2009 Justin Maggard
|
||||||
* This software is subject to the conditions detailed in the
|
* This software is subject to the conditions detailed in the
|
||||||
* LICENCE file provided in this distribution.
|
* LICENCE file provided in this distribution.
|
||||||
* */
|
* */
|
||||||
#ifndef __UTILS_H__
|
#ifndef __UTILS_H__
|
||||||
#define __UTILS_H__
|
#define __UTILS_H__
|
||||||
#include <gd.h>
|
|
||||||
|
|
||||||
int
|
int
|
||||||
ends_with(const char * haystack, const char * needle);
|
ends_with(const char * haystack, const char * needle);
|
||||||
@ -26,9 +25,4 @@ strip_ext(char * name);
|
|||||||
int
|
int
|
||||||
make_dir(char * path, mode_t mode);
|
make_dir(char * path, mode_t mode);
|
||||||
|
|
||||||
/* A GD resize function with a good balance of quality and speed */
|
|
||||||
void
|
|
||||||
boxfilter_resize(gdImagePtr dst, gdImagePtr src,
|
|
||||||
int dstX, int dstY, int srcX, int srcY,
|
|
||||||
int dstW, int dstH, int srcW, int srcH);
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
x
Reference in New Issue
Block a user