added resizer
This commit is contained in:
58
lib/danbooru_image_resizer/GIFReader.cpp
Normal file
58
lib/danbooru_image_resizer/GIFReader.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#include <string.h>
|
||||
#include <gd.h>
|
||||
#include "GIFReader.h"
|
||||
#include "RowBuffer.h"
|
||||
#include "Resize.h"
|
||||
|
||||
bool GIF::Read(FILE *f, Resizer *resizer, char error[1024])
|
||||
{
|
||||
RowBuffer Rows;
|
||||
bool Ret = false;
|
||||
gdImage *image = gdImageCreateFromGif(f);
|
||||
|
||||
if(!image)
|
||||
{
|
||||
strcpy(error, "couldn't read GIF");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!Rows.Init(image->sx, image->sy, 3))
|
||||
{
|
||||
strcpy(error, "out of memory");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
resizer->SetSource(image->sx, image->sy, 3);
|
||||
for(int y = 0; y < image->sy; ++y)
|
||||
{
|
||||
uint8_t *p = Rows.GetRow(y);
|
||||
if(p == NULL)
|
||||
{
|
||||
strcpy(error, "out of memory");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for(int x = 0; x < image->sx; ++x)
|
||||
{
|
||||
int c = gdImageGetTrueColorPixel(image, x, y);
|
||||
(*p++) = gdTrueColorGetRed(c);
|
||||
(*p++) = gdTrueColorGetGreen(c);
|
||||
(*p++) = gdTrueColorGetBlue(c);
|
||||
}
|
||||
|
||||
int DiscardRow;
|
||||
if(!resizer->Run(Rows.GetRows(), Rows.GetStartRow(), Rows.GetEndRow(), DiscardRow))
|
||||
{
|
||||
strcpy(error, resizer->GetError());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
Rows.DiscardRows(DiscardRow);
|
||||
}
|
||||
|
||||
Ret = true;
|
||||
|
||||
cleanup:
|
||||
gdImageDestroy(image);
|
||||
return Ret;
|
||||
}
|
||||
11
lib/danbooru_image_resizer/GIFReader.h
Normal file
11
lib/danbooru_image_resizer/GIFReader.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#ifndef GIF_READER_H
|
||||
#define GIF_READER_H
|
||||
|
||||
#include "Reader.h"
|
||||
class GIF: public Reader
|
||||
{
|
||||
public:
|
||||
bool Read(FILE *f, Resizer *resizer, char error[1024]);
|
||||
};
|
||||
|
||||
#endif
|
||||
155
lib/danbooru_image_resizer/JPEGReader.cpp
Normal file
155
lib/danbooru_image_resizer/JPEGReader.cpp
Normal file
@@ -0,0 +1,155 @@
|
||||
#include <string.h>
|
||||
#include "JPEGReader.h"
|
||||
#include "RowBuffer.h"
|
||||
#include "Resize.h"
|
||||
#include <algorithm>
|
||||
using namespace std;
|
||||
|
||||
static void jpeg_error_exit(j_common_ptr CInfo)
|
||||
{
|
||||
jpeg_error *myerr = (jpeg_error *) CInfo->err;
|
||||
(*CInfo->err->format_message) (CInfo, myerr->buffer);
|
||||
longjmp(myerr->setjmp_buffer, 1);
|
||||
}
|
||||
|
||||
static void jpeg_warning(j_common_ptr cinfo, int msg_level)
|
||||
{
|
||||
}
|
||||
|
||||
JPEGCompressor::JPEGCompressor(FILE *f)
|
||||
{
|
||||
m_File = f;
|
||||
memset(&m_CInfo, 0, sizeof(m_CInfo));
|
||||
}
|
||||
|
||||
JPEGCompressor::~JPEGCompressor()
|
||||
{
|
||||
jpeg_destroy_compress(&m_CInfo);
|
||||
}
|
||||
|
||||
const char *JPEGCompressor::GetError() const
|
||||
{
|
||||
return m_JErr.buffer;
|
||||
}
|
||||
|
||||
|
||||
bool JPEGCompressor::Init(int width, int height, int quality)
|
||||
{
|
||||
m_CInfo.err = jpeg_std_error(&m_JErr.pub);
|
||||
|
||||
m_JErr.pub.error_exit = jpeg_error_exit;
|
||||
m_JErr.pub.emit_message = jpeg_warning;
|
||||
|
||||
if(setjmp(m_JErr.setjmp_buffer))
|
||||
return false;
|
||||
|
||||
jpeg_create_compress(&m_CInfo);
|
||||
|
||||
jpeg_stdio_dest(&m_CInfo, m_File);
|
||||
|
||||
m_CInfo.image_width = width;
|
||||
m_CInfo.image_height = height;
|
||||
m_CInfo.input_components = 3; /* # of color components per pixel */
|
||||
m_CInfo.in_color_space = JCS_RGB; /* colorspace of input image */
|
||||
|
||||
jpeg_set_defaults(&m_CInfo);
|
||||
jpeg_simple_progression(&m_CInfo);
|
||||
jpeg_set_quality(&m_CInfo, quality, TRUE); // limit to baseline-JPEG values
|
||||
|
||||
jpeg_start_compress(&m_CInfo, TRUE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int JPEGCompressor::GetWidth() const
|
||||
{
|
||||
return m_CInfo.image_width;
|
||||
}
|
||||
|
||||
int JPEGCompressor::GetHeight() const
|
||||
{
|
||||
return m_CInfo.image_height;
|
||||
}
|
||||
|
||||
bool JPEGCompressor::WriteRow(uint8_t *row)
|
||||
{
|
||||
if(setjmp(m_JErr.setjmp_buffer))
|
||||
return false;
|
||||
|
||||
jpeg_write_scanlines(&m_CInfo, (JSAMPLE **) &row, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JPEGCompressor::Finish()
|
||||
{
|
||||
if(setjmp(m_JErr.setjmp_buffer))
|
||||
return false;
|
||||
|
||||
jpeg_finish_compress(&m_CInfo);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool JPEG::Read(FILE *f, Resizer *resizer, char error[1024])
|
||||
{
|
||||
// JMSG_LENGTH_MAX <= sizeof(error)
|
||||
m_JErr.buffer = error;
|
||||
RowBuffer Rows;
|
||||
|
||||
m_Resizer = resizer;
|
||||
|
||||
struct jpeg_decompress_struct CInfo;
|
||||
CInfo.err = jpeg_std_error(&m_JErr.pub);
|
||||
m_JErr.pub.error_exit = jpeg_error_exit;
|
||||
m_JErr.pub.emit_message = jpeg_warning;
|
||||
|
||||
bool Ret = false;
|
||||
if(setjmp(m_JErr.setjmp_buffer))
|
||||
goto cleanup;
|
||||
|
||||
jpeg_create_decompress(&CInfo);
|
||||
|
||||
jpeg_stdio_src(&CInfo, f);
|
||||
jpeg_read_header(&CInfo, TRUE);
|
||||
CInfo.out_color_space = JCS_RGB;
|
||||
|
||||
jpeg_start_decompress(&CInfo);
|
||||
|
||||
if(!Rows.Init(CInfo.output_width, CInfo.output_height, 3))
|
||||
{
|
||||
strcpy(error, "out of memory");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
m_Resizer->SetSource(CInfo.output_width, CInfo.output_height, 3);
|
||||
|
||||
while(CInfo.output_scanline < CInfo.output_height)
|
||||
{
|
||||
uint8_t *p = Rows.GetRow(CInfo.output_scanline);
|
||||
if(p == NULL)
|
||||
{
|
||||
strcpy(error, "out of memory");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
jpeg_read_scanlines(&CInfo, &p, 1);
|
||||
|
||||
int DiscardRow;
|
||||
if(!m_Resizer->Run(Rows.GetRows(), Rows.GetStartRow(), min(Rows.GetEndRow(), (int) CInfo.output_scanline+1), DiscardRow))
|
||||
{
|
||||
strcpy(error, m_Resizer->GetError());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
Rows.DiscardRows(DiscardRow);
|
||||
}
|
||||
|
||||
jpeg_finish_decompress(&CInfo);
|
||||
|
||||
Ret = true;
|
||||
|
||||
cleanup:
|
||||
jpeg_destroy_decompress(&CInfo);
|
||||
|
||||
return Ret;
|
||||
}
|
||||
|
||||
47
lib/danbooru_image_resizer/JPEGReader.h
Normal file
47
lib/danbooru_image_resizer/JPEGReader.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#ifndef JPEG_READER_H
|
||||
#define JPEG_READER_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <setjmp.h>
|
||||
#include "jpeglib-extern.h"
|
||||
#include "Reader.h"
|
||||
|
||||
struct jpeg_error
|
||||
{
|
||||
struct jpeg_error_mgr pub;
|
||||
jmp_buf setjmp_buffer;
|
||||
char *buffer;
|
||||
};
|
||||
|
||||
class JPEG: public Reader
|
||||
{
|
||||
public:
|
||||
bool Read(FILE *f, Resizer *resizer, char error[1024]);
|
||||
|
||||
private:
|
||||
Resizer *m_Resizer;
|
||||
struct jpeg_error m_JErr;
|
||||
};
|
||||
|
||||
class JPEGCompressor
|
||||
{
|
||||
public:
|
||||
JPEGCompressor(FILE *f);
|
||||
~JPEGCompressor();
|
||||
|
||||
bool Init(int width, int height, int quality);
|
||||
bool WriteRow(uint8_t *row);
|
||||
bool Finish();
|
||||
|
||||
int GetWidth() const;
|
||||
int GetHeight() const;
|
||||
const char *GetError() const;
|
||||
|
||||
private:
|
||||
FILE *m_File;
|
||||
struct jpeg_compress_struct m_CInfo;
|
||||
struct jpeg_error m_JErr;
|
||||
};
|
||||
|
||||
#endif
|
||||
157
lib/danbooru_image_resizer/Makefile
Normal file
157
lib/danbooru_image_resizer/Makefile
Normal file
@@ -0,0 +1,157 @@
|
||||
|
||||
SHELL = /bin/sh
|
||||
|
||||
#### Start of system configuration section. ####
|
||||
|
||||
srcdir = .
|
||||
topdir = /opt/local/lib/ruby/1.8/i686-darwin10
|
||||
hdrdir = $(topdir)
|
||||
VPATH = $(srcdir):$(topdir):$(hdrdir)
|
||||
exec_prefix = $(prefix)
|
||||
prefix = $(DESTDIR)/opt/local
|
||||
sharedstatedir = $(prefix)/com
|
||||
mandir = $(DESTDIR)/opt/local/share/man
|
||||
psdir = $(docdir)
|
||||
oldincludedir = $(DESTDIR)/usr/include
|
||||
localedir = $(datarootdir)/locale
|
||||
bindir = $(exec_prefix)/bin
|
||||
libexecdir = $(exec_prefix)/libexec
|
||||
sitedir = $(libdir)/ruby/site_ruby
|
||||
htmldir = $(docdir)
|
||||
vendorarchdir = $(vendorlibdir)/$(sitearch)
|
||||
includedir = $(prefix)/include
|
||||
infodir = $(datarootdir)/info
|
||||
vendorlibdir = $(vendordir)/$(ruby_version)
|
||||
sysconfdir = $(prefix)/etc
|
||||
libdir = $(exec_prefix)/lib
|
||||
sbindir = $(exec_prefix)/sbin
|
||||
rubylibdir = $(libdir)/ruby/$(ruby_version)
|
||||
docdir = $(datarootdir)/doc/$(PACKAGE)
|
||||
dvidir = $(docdir)
|
||||
vendordir = $(DESTDIR)/opt/local/lib/ruby/vendor_ruby
|
||||
datarootdir = $(prefix)/share
|
||||
pdfdir = $(docdir)
|
||||
archdir = $(rubylibdir)/$(arch)
|
||||
sitearchdir = $(sitelibdir)/$(sitearch)
|
||||
datadir = $(datarootdir)
|
||||
localstatedir = $(prefix)/var
|
||||
sitelibdir = $(sitedir)/$(ruby_version)
|
||||
|
||||
CC = g++
|
||||
LIBRUBY = $(LIBRUBY_SO)
|
||||
LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
|
||||
LIBRUBYARG_SHARED = -l$(RUBY_SO_NAME)
|
||||
LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)-static
|
||||
|
||||
RUBY_EXTCONF_H =
|
||||
CFLAGS = -fno-common -O2 -fno-exceptions -Wall -arch x86_64
|
||||
INCFLAGS = -I. -I. -I/opt/local/lib/ruby/1.8/i686-darwin10 -I.
|
||||
DEFS =
|
||||
CPPFLAGS = -DHAVE_GD_H -DHAVE_GDIMAGECREATEFROMGIF -DHAVE_GDIMAGEJPEG -DHAVE_JPEG_SET_QUALITY -DHAVE_PNG_SET_EXPAND_GRAY_1_2_4_TO_8 -I/opt/local/include -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -I/opt/local/include
|
||||
CXXFLAGS = $(CFLAGS)
|
||||
ldflags = -L. -L/opt/local/lib
|
||||
dldflags =
|
||||
archflag = -arch x86_64
|
||||
DLDFLAGS = $(ldflags) $(dldflags) $(archflag)
|
||||
LDSHARED = $(CC) -dynamic -bundle -undefined suppress -flat_namespace
|
||||
AR = ar
|
||||
EXEEXT =
|
||||
|
||||
RUBY_INSTALL_NAME = ruby
|
||||
RUBY_SO_NAME = ruby
|
||||
arch = i686-darwin10
|
||||
sitearch = i686-darwin10
|
||||
ruby_version = 1.8
|
||||
ruby = /opt/local/bin/ruby
|
||||
RUBY = $(ruby)
|
||||
RM = rm -f
|
||||
MAKEDIRS = mkdir -p
|
||||
INSTALL = /usr/bin/install -c
|
||||
INSTALL_PROG = $(INSTALL) -m 0755
|
||||
INSTALL_DATA = $(INSTALL) -m 644
|
||||
COPY = cp
|
||||
|
||||
#### End of system configuration section. ####
|
||||
|
||||
preload =
|
||||
|
||||
libpath = . $(libdir)
|
||||
LIBPATH = -L. -L$(libdir)
|
||||
DEFFILE =
|
||||
|
||||
CLEANFILES = mkmf.log
|
||||
DISTCLEANFILES =
|
||||
|
||||
extout =
|
||||
extout_prefix =
|
||||
target_prefix =
|
||||
LOCAL_LIBS =
|
||||
LIBS = $(LIBRUBYARG_SHARED) -lpng -ljpeg -lgd -lpthread -ldl -lobjc
|
||||
SRCS = danbooru_image_resizer.cpp GIFReader.cpp JPEGReader.cpp PNGReader.cpp Resize.cpp RowBuffer.cpp
|
||||
OBJS = danbooru_image_resizer.o GIFReader.o JPEGReader.o PNGReader.o Resize.o RowBuffer.o
|
||||
TARGET = danbooru_image_resizer
|
||||
DLLIB = $(TARGET).bundle
|
||||
EXTSTATIC =
|
||||
STATIC_LIB =
|
||||
|
||||
BINDIR = $(bindir)
|
||||
RUBYCOMMONDIR = $(sitedir)$(target_prefix)
|
||||
RUBYLIBDIR = $(sitelibdir)$(target_prefix)
|
||||
RUBYARCHDIR = $(sitearchdir)$(target_prefix)
|
||||
|
||||
TARGET_SO = $(DLLIB)
|
||||
CLEANLIBS = $(TARGET).bundle $(TARGET).il? $(TARGET).tds $(TARGET).map
|
||||
CLEANOBJS = *.o *.a *.s[ol] *.pdb *.exp *.bak
|
||||
|
||||
all: $(DLLIB)
|
||||
static: $(STATIC_LIB)
|
||||
|
||||
clean:
|
||||
@-$(RM) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES)
|
||||
|
||||
distclean: clean
|
||||
@-$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
|
||||
@-$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)
|
||||
|
||||
realclean: distclean
|
||||
install: install-so install-rb
|
||||
|
||||
install-so: $(RUBYARCHDIR)
|
||||
install-so: $(RUBYARCHDIR)/$(DLLIB)
|
||||
$(RUBYARCHDIR)/$(DLLIB): $(DLLIB)
|
||||
$(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
|
||||
install-rb: pre-install-rb install-rb-default
|
||||
install-rb-default: pre-install-rb-default
|
||||
pre-install-rb: Makefile
|
||||
pre-install-rb-default: Makefile
|
||||
$(RUBYARCHDIR):
|
||||
$(MAKEDIRS) $@
|
||||
|
||||
site-install: site-install-so site-install-rb
|
||||
site-install-so: install-so
|
||||
site-install-rb: install-rb
|
||||
|
||||
.SUFFIXES: .c .m .cc .cxx .cpp .C .o
|
||||
|
||||
.cc.o:
|
||||
$(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<
|
||||
|
||||
.cxx.o:
|
||||
$(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<
|
||||
|
||||
.cpp.o:
|
||||
$(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<
|
||||
|
||||
.C.o:
|
||||
$(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) -c $<
|
||||
|
||||
.c.o:
|
||||
$(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) -c $<
|
||||
|
||||
$(DLLIB): $(OBJS) Makefile
|
||||
@-$(RM) $@
|
||||
$(LDSHARED) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)
|
||||
|
||||
|
||||
|
||||
$(OBJS): ruby.h defines.h
|
||||
138
lib/danbooru_image_resizer/PNGReader.cpp
Normal file
138
lib/danbooru_image_resizer/PNGReader.cpp
Normal file
@@ -0,0 +1,138 @@
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include "PNGReader.h"
|
||||
#include "Resize.h"
|
||||
#include <algorithm>
|
||||
using namespace std;
|
||||
|
||||
void PNG::Error(png_struct *png, const char *error)
|
||||
{
|
||||
png_error_info *info = (png_error_info *) png->error_ptr;
|
||||
strncpy(info->err, error, 1024);
|
||||
info->err[1023] = 0;
|
||||
longjmp(png->jmpbuf, 1);
|
||||
}
|
||||
|
||||
void PNG::Warning(png_struct *png, const char *warning)
|
||||
{
|
||||
}
|
||||
|
||||
void PNG::InfoCallback(png_struct *png, png_info *info_ptr)
|
||||
{
|
||||
PNG *data = (PNG *) png_get_progressive_ptr(png);
|
||||
|
||||
png_uint_32 width, height;
|
||||
int bit_depth, color_type;
|
||||
png_get_IHDR(png, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL);
|
||||
|
||||
png_set_palette_to_rgb(png);
|
||||
png_set_tRNS_to_alpha(png);
|
||||
png_set_filler(png, 0xFF, PNG_FILLER_AFTER);
|
||||
if(bit_depth < 8)
|
||||
png_set_packing(png);
|
||||
if(color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
|
||||
png_set_expand_gray_1_2_4_to_8(png);
|
||||
if(bit_depth == 16)
|
||||
png_set_strip_16(png);
|
||||
data->m_Passes = png_set_interlace_handling(png);
|
||||
|
||||
if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
|
||||
png_set_gray_to_rgb(png);
|
||||
|
||||
if(!data->m_Rows.Init(width, height, 4))
|
||||
Error(png, "out of memory");
|
||||
|
||||
png_read_update_info(png, info_ptr);
|
||||
|
||||
data->m_Resizer->SetSource(width, height, 4);
|
||||
}
|
||||
|
||||
void PNG::RowCallback(png_struct *png, png_byte *new_row, png_uint_32 row_num, int pass)
|
||||
{
|
||||
PNG *data = (PNG *) png_get_progressive_ptr(png);
|
||||
|
||||
uint8_t *p = data->m_Rows.GetRow(row_num);
|
||||
if(p == NULL)
|
||||
Error(png, "out of memory");
|
||||
|
||||
png_progressive_combine_row(png, p, new_row);
|
||||
|
||||
if(pass != data->m_Passes - 1)
|
||||
return;
|
||||
|
||||
/* We've allocated data->m_RowsAllocated, but if we're doing multiple passes, only
|
||||
* rows 0 to row_num will actually have usable data. */
|
||||
int DiscardRow;
|
||||
int LastRow = min(data->m_Rows.GetEndRow(), (int) row_num+1);
|
||||
if(!data->m_Resizer->Run(data->m_Rows.GetRows(), data->m_Rows.GetStartRow(), LastRow, DiscardRow))
|
||||
Error(png, data->m_Resizer->GetError());
|
||||
|
||||
/* If we're interlaced, never discard rows. */
|
||||
if(data->m_Passes == 1)
|
||||
data->m_Rows.DiscardRows(DiscardRow);
|
||||
}
|
||||
|
||||
void PNG::EndCallback(png_struct *png, png_info *info)
|
||||
{
|
||||
PNG *data = (PNG *) png_get_progressive_ptr(png);
|
||||
data->m_Done = true;
|
||||
}
|
||||
|
||||
|
||||
bool PNG::Read(FILE *f, Resizer *resizer, char error[1024])
|
||||
{
|
||||
m_Resizer = resizer;
|
||||
|
||||
png_error_info err;
|
||||
err.err = error;
|
||||
|
||||
png_struct *png = png_create_read_struct(PNG_LIBPNG_VER_STRING, &err, Error, Warning);
|
||||
if(png == NULL)
|
||||
{
|
||||
sprintf(error, "creating png_create_read_struct failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
png_info *info_ptr = png_create_info_struct(png);
|
||||
if(info_ptr == NULL)
|
||||
{
|
||||
png_destroy_read_struct(&png, NULL, NULL);
|
||||
sprintf(error, "creating png_create_info_struct failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(setjmp(png->jmpbuf))
|
||||
{
|
||||
png_destroy_read_struct(&png, &info_ptr, NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
png_set_progressive_read_fn(png, this, InfoCallback, RowCallback, EndCallback);
|
||||
|
||||
while(1)
|
||||
{
|
||||
png_byte buf[1024*16];
|
||||
int ret = fread(buf, 1, sizeof(buf), f);
|
||||
if(ret == 0)
|
||||
break;
|
||||
if(ferror(f))
|
||||
{
|
||||
strcpy(error, strerror(errno));
|
||||
png_destroy_read_struct(&png, &info_ptr, NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
png_process_data(png, info_ptr, buf, ret);
|
||||
}
|
||||
|
||||
if(!m_Done)
|
||||
{
|
||||
strcpy(error, "incomplete file");
|
||||
png_destroy_read_struct(&png, &info_ptr, NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
png_destroy_read_struct(&png, &info_ptr, NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
37
lib/danbooru_image_resizer/PNGReader.h
Normal file
37
lib/danbooru_image_resizer/PNGReader.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#ifndef PNG_READER_H
|
||||
#define PNG_READER_H
|
||||
|
||||
#include <png.h>
|
||||
#include "Reader.h"
|
||||
#include "RowBuffer.h"
|
||||
|
||||
struct png_error_info
|
||||
{
|
||||
char *err;
|
||||
};
|
||||
|
||||
class PNG: public Reader
|
||||
{
|
||||
public:
|
||||
PNG()
|
||||
{
|
||||
m_Done = false;
|
||||
}
|
||||
|
||||
bool Read(FILE *f, Resizer *resizer, char error[1024]);
|
||||
|
||||
private:
|
||||
RowBuffer m_Rows;
|
||||
Resizer *m_Resizer;
|
||||
|
||||
bool m_Done;
|
||||
int m_Passes;
|
||||
|
||||
static void Error(png_struct *png, const char *error);
|
||||
static void Warning(png_struct *png, const char *warning);
|
||||
static void InfoCallback(png_struct *png, png_info *info_ptr);
|
||||
static void RowCallback(png_struct *png, png_byte *new_row, png_uint_32 row_num, int pass);
|
||||
static void EndCallback(png_struct *png, png_info *info);
|
||||
};
|
||||
|
||||
#endif
|
||||
14
lib/danbooru_image_resizer/Reader.h
Normal file
14
lib/danbooru_image_resizer/Reader.h
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef READER_H
|
||||
#define READER_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
class Resizer;
|
||||
class Reader
|
||||
{
|
||||
public:
|
||||
virtual ~Reader() { }
|
||||
virtual bool Read(FILE *f, Resizer *rp, char errorbuf[1024]) = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
184
lib/danbooru_image_resizer/Resize.cpp
Normal file
184
lib/danbooru_image_resizer/Resize.cpp
Normal file
@@ -0,0 +1,184 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "Resize.h"
|
||||
#include "JPEGReader.h"
|
||||
#include <algorithm>
|
||||
using namespace std;
|
||||
|
||||
Resizer::Resizer(JPEGCompressor *Compressor)
|
||||
{
|
||||
m_Compressor = Compressor;
|
||||
m_CurrentY = 0;
|
||||
m_OutBuf = NULL;
|
||||
}
|
||||
|
||||
Resizer::~Resizer()
|
||||
{
|
||||
if(m_OutBuf)
|
||||
free(m_OutBuf);
|
||||
}
|
||||
|
||||
const char *Resizer::GetError() const
|
||||
{
|
||||
return m_Compressor->GetError();
|
||||
}
|
||||
|
||||
void Resizer::SetSource(int Width, int Height, int BPP)
|
||||
{
|
||||
m_SourceWidth = Width;
|
||||
m_SourceHeight = Height;
|
||||
m_SourceBPP = BPP;
|
||||
}
|
||||
|
||||
bool Resizer::SetDest(int Width, int Height, int Quality)
|
||||
{
|
||||
m_DestWidth = Width;
|
||||
m_DestHeight = Height;
|
||||
m_OutBuf = (uint8_t *) malloc(Width*3);
|
||||
|
||||
return m_Compressor->Init(Width, Height, Quality);
|
||||
}
|
||||
|
||||
#define scale(x, l1, h1, l2, h2) (((x)-(l1))*((h2)-(l2))/((h1)-(l1))+(l2))
|
||||
|
||||
static void Average(const uint8_t *const *src, float Colors[3], float SourceXStart, float SourceXEnd, float SourceYStart, float SourceYEnd, int SourceBPP)
|
||||
{
|
||||
float Total = 0.0f;
|
||||
for(float y = SourceYStart; y < SourceYEnd; ++y)
|
||||
{
|
||||
float YCoverage = 1.0f;
|
||||
if(int(y) == int(SourceYStart))
|
||||
YCoverage -= y - int(y);
|
||||
if(int(y) == int(SourceYEnd))
|
||||
YCoverage -= 1.0f - (SourceYEnd - int(SourceYEnd));
|
||||
|
||||
const uint8_t *xsrc=src[(int) y]+(int)SourceXStart*SourceBPP;
|
||||
|
||||
/* The two conditionals can only be true on the first and last iteration of the loop,
|
||||
* so unfold those iterations and pull the conditionals out of the inner loop. */
|
||||
/* while(x < SourceXEnd)
|
||||
{
|
||||
float XCoverage = 1.0f;
|
||||
if(int(x) == int(SourceXStart))
|
||||
XCoverage -= x - int(x);
|
||||
if(int(x) == int(SourceXEnd))
|
||||
XCoverage -= 1.0f - (SourceXEnd - int(SourceXEnd));
|
||||
|
||||
Colors[0] += xsrc[0] * XCoverage * YCoverage;
|
||||
Colors[1] += xsrc[1] * XCoverage * YCoverage;
|
||||
Colors[2] += xsrc[2] * XCoverage * YCoverage;
|
||||
if(SourceBPP == 4)
|
||||
Colors[3] += xsrc[3] * XCoverage * YCoverage;
|
||||
xsrc += SourceBPP;
|
||||
|
||||
Total += XCoverage * YCoverage;
|
||||
++x;
|
||||
}
|
||||
*/
|
||||
float x = int(SourceXStart);
|
||||
if(x < SourceXEnd)
|
||||
{
|
||||
float XCoverage = 1.0f;
|
||||
if(int(x) == int(SourceXStart))
|
||||
XCoverage -= x - int(x);
|
||||
if(int(x) == int(SourceXEnd))
|
||||
XCoverage -= 1.0f - (SourceXEnd - int(SourceXEnd));
|
||||
|
||||
Colors[0] += xsrc[0] * XCoverage * YCoverage;
|
||||
Colors[1] += xsrc[1] * XCoverage * YCoverage;
|
||||
Colors[2] += xsrc[2] * XCoverage * YCoverage;
|
||||
if(SourceBPP == 4)
|
||||
Colors[3] += xsrc[3] * XCoverage * YCoverage;
|
||||
xsrc += SourceBPP;
|
||||
|
||||
Total += XCoverage * YCoverage;
|
||||
++x;
|
||||
}
|
||||
|
||||
while(x < SourceXEnd-1)
|
||||
{
|
||||
Colors[0] += xsrc[0] * YCoverage;
|
||||
Colors[1] += xsrc[1] * YCoverage;
|
||||
Colors[2] += xsrc[2] * YCoverage;
|
||||
if(SourceBPP == 4)
|
||||
Colors[3] += xsrc[3] * YCoverage;
|
||||
xsrc += SourceBPP;
|
||||
|
||||
Total += YCoverage;
|
||||
++x;
|
||||
}
|
||||
|
||||
if(x < SourceXEnd)
|
||||
{
|
||||
float XCoverage = 1.0f;
|
||||
if(int(x) == int(SourceXStart))
|
||||
XCoverage -= x - int(x);
|
||||
if(int(x) == int(SourceXEnd))
|
||||
XCoverage -= 1.0f - (SourceXEnd - int(SourceXEnd));
|
||||
|
||||
Colors[0] += xsrc[0] * XCoverage * YCoverage;
|
||||
Colors[1] += xsrc[1] * XCoverage * YCoverage;
|
||||
Colors[2] += xsrc[2] * XCoverage * YCoverage;
|
||||
if(SourceBPP == 4)
|
||||
Colors[3] += xsrc[3] * XCoverage * YCoverage;
|
||||
xsrc += SourceBPP;
|
||||
|
||||
Total += XCoverage * YCoverage;
|
||||
}
|
||||
}
|
||||
|
||||
if(Total != 0.0f)
|
||||
for(int i = 0; i < 4; ++i)
|
||||
Colors[i] /= Total;
|
||||
}
|
||||
|
||||
bool Resizer::Run(const uint8_t *const *Source, int StartRow, int EndRow, int &DiscardRow)
|
||||
{
|
||||
while(m_CurrentY < m_DestHeight)
|
||||
{
|
||||
float SourceYStart = scale((float) m_CurrentY, 0.0f, (float) m_DestHeight, 0.0f, (float) m_SourceHeight);
|
||||
float SourceYEnd = scale((float) m_CurrentY + 1, 0.0f, (float) m_DestHeight, 0.0f, (float) m_SourceHeight);
|
||||
DiscardRow = int(SourceYStart)-1;
|
||||
|
||||
if(EndRow != m_SourceHeight && int(SourceYEnd)+1 > EndRow-1)
|
||||
return true;
|
||||
assert(SourceYStart>=StartRow);
|
||||
|
||||
uint8_t *Output = m_OutBuf;
|
||||
for(int x = 0; x < m_DestWidth; ++x)
|
||||
{
|
||||
float SourceXStart = scale((float) x, 0.0f, (float) m_DestWidth, 0.0f, (float) m_SourceWidth);
|
||||
float SourceXEnd = scale((float) x + 1, 0.0f, (float) m_DestWidth, 0.0f, (float) m_SourceWidth);
|
||||
|
||||
float Colors[4] = { 0.0 };
|
||||
Average(Source, Colors, SourceXStart, SourceXEnd, SourceYStart, SourceYEnd, m_SourceBPP);
|
||||
|
||||
if(m_SourceBPP == 4)
|
||||
{
|
||||
for(int i = 0; i < 3; ++i)
|
||||
Colors[i] *= Colors[3]/255.0f;
|
||||
}
|
||||
|
||||
Output[0] = (uint8_t) min(255, int(Colors[0]));
|
||||
Output[1] = (uint8_t) min(255, int(Colors[1]));
|
||||
Output[2] = (uint8_t) min(255, int(Colors[2]));
|
||||
|
||||
Output += 3;
|
||||
}
|
||||
|
||||
if(!m_Compressor->WriteRow((JSAMPLE *) m_OutBuf))
|
||||
return false;
|
||||
++m_CurrentY;
|
||||
}
|
||||
|
||||
if(m_CurrentY == m_DestHeight)
|
||||
{
|
||||
if(!m_Compressor->Finish())
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
41
lib/danbooru_image_resizer/Resize.h
Normal file
41
lib/danbooru_image_resizer/Resize.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#ifndef RESIZE_H
|
||||
#define RESIZE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
class JPEGCompressor;
|
||||
|
||||
class Resizer
|
||||
{
|
||||
public:
|
||||
Resizer(JPEGCompressor *Compressor);
|
||||
~Resizer();
|
||||
|
||||
// BPP is 3 or 4, indicating RGB or RGBA.
|
||||
void SetSource(int Width, int Height, int BPP);
|
||||
bool SetDest(int Width, int Height, int Quality);
|
||||
|
||||
/*
|
||||
* Resize part of an image.
|
||||
*
|
||||
* [FirstRow,LastRow) is a range indicating which elements in src[] are available.
|
||||
* On return, any rows in [0,DiscardRow) are no longer needed and can be deleted.
|
||||
*/
|
||||
bool Run(const uint8_t *const *src, int FirstRow, int LastRow, int &DiscardRow);
|
||||
const char *GetError() const;
|
||||
|
||||
private:
|
||||
JPEGCompressor *m_Compressor;
|
||||
uint8_t *m_OutBuf;
|
||||
|
||||
int m_SourceWidth;
|
||||
int m_SourceHeight;
|
||||
int m_SourceBPP;
|
||||
|
||||
int m_DestWidth;
|
||||
int m_DestHeight;
|
||||
|
||||
float m_CurrentY;
|
||||
};
|
||||
|
||||
#endif
|
||||
81
lib/danbooru_image_resizer/RowBuffer.cpp
Normal file
81
lib/danbooru_image_resizer/RowBuffer.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "RowBuffer.h"
|
||||
#include <algorithm>
|
||||
using namespace std;
|
||||
|
||||
RowBuffer::RowBuffer()
|
||||
{
|
||||
m_Rows = NULL;
|
||||
m_StartRow = 0;
|
||||
m_EndRow = 0;
|
||||
m_BPP = 0;
|
||||
m_Height = 0;
|
||||
}
|
||||
|
||||
RowBuffer::~RowBuffer()
|
||||
{
|
||||
for(int i = 0; i < m_Height; ++i)
|
||||
delete [] m_Rows[i];
|
||||
|
||||
delete [] m_Rows;
|
||||
}
|
||||
|
||||
bool RowBuffer::Init(int Width, int Height, int BPP)
|
||||
{
|
||||
m_Width = Width;
|
||||
m_Height = Height;
|
||||
m_BPP = BPP;
|
||||
|
||||
m_Rows = new uint8_t *[Height];
|
||||
if(m_Rows == NULL)
|
||||
return false;
|
||||
memset(m_Rows, 0, sizeof(uint8_t *) * Height);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t *RowBuffer::GetRow(int Row)
|
||||
{
|
||||
assert(m_BPP > 0);
|
||||
|
||||
if(m_Rows[Row] == NULL)
|
||||
{
|
||||
m_Rows[Row] = new uint8_t[m_Width*m_BPP];
|
||||
if(m_Rows[Row] == NULL)
|
||||
return NULL;
|
||||
if(m_StartRow == m_EndRow)
|
||||
{
|
||||
m_StartRow = Row;
|
||||
m_EndRow = m_StartRow + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(int(Row) == m_StartRow+1)
|
||||
{
|
||||
while(m_StartRow != 0 && m_Rows[m_StartRow-1])
|
||||
--m_StartRow;
|
||||
}
|
||||
|
||||
if(int(Row) == m_EndRow)
|
||||
{
|
||||
while(m_EndRow < m_Height && m_Rows[m_EndRow])
|
||||
++m_EndRow;
|
||||
}
|
||||
return m_Rows[Row];
|
||||
}
|
||||
|
||||
void RowBuffer::DiscardRows(int DiscardRow)
|
||||
{
|
||||
assert(m_BPP > 0);
|
||||
|
||||
for(int i = m_StartRow; i < DiscardRow; ++i)
|
||||
{
|
||||
delete [] m_Rows[i];
|
||||
m_Rows[i] = NULL;
|
||||
}
|
||||
|
||||
m_StartRow = max(m_StartRow, DiscardRow);
|
||||
m_EndRow = max(m_EndRow, DiscardRow);
|
||||
}
|
||||
40
lib/danbooru_image_resizer/RowBuffer.h
Normal file
40
lib/danbooru_image_resizer/RowBuffer.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#ifndef ROW_BUFFER_H
|
||||
#define ROW_BUFFER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
class RowBuffer
|
||||
{
|
||||
public:
|
||||
RowBuffer();
|
||||
~RowBuffer();
|
||||
|
||||
bool Init(int Width, int Height, int BPP);
|
||||
|
||||
/* Return row, allocating if necessary. */
|
||||
uint8_t *GetRow(int row);
|
||||
|
||||
// Free rows [0,DiscardRow).
|
||||
void DiscardRows(int DiscardRow);
|
||||
|
||||
/* Get a range of rows allocated in m_Rows: [m_StartRow,m_EndRow). If
|
||||
* more than one allocated range exists, which range is returned is undefined. */
|
||||
int GetStartRow() const { return m_StartRow; }
|
||||
int GetEndRow() const { return m_EndRow; }
|
||||
const uint8_t *const *GetRows() const { return m_Rows; }
|
||||
|
||||
private:
|
||||
/* Array of image rows. These are allocated as needed. */
|
||||
uint8_t **m_Rows;
|
||||
|
||||
/* in m_Rows is allocated: */
|
||||
int m_StartRow;
|
||||
int m_EndRow;
|
||||
|
||||
int m_Width;
|
||||
int m_Height;
|
||||
int m_BPP;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
84
lib/danbooru_image_resizer/danbooru_image_resizer.cpp
Normal file
84
lib/danbooru_image_resizer/danbooru_image_resizer.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
#include <ruby.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "PNGReader.h"
|
||||
#include "GIFReader.h"
|
||||
#include "JPEGReader.h"
|
||||
#include "Resize.h"
|
||||
|
||||
static VALUE danbooru_module;
|
||||
|
||||
static VALUE danbooru_resize_image(VALUE module, VALUE file_ext_val, VALUE read_path_val, VALUE write_path_val, VALUE output_width_val, VALUE output_height_val, VALUE output_quality_val)
|
||||
{
|
||||
const char * file_ext = StringValueCStr(file_ext_val);
|
||||
const char * read_path = StringValueCStr(read_path_val);
|
||||
const char * write_path = StringValueCStr(write_path_val);
|
||||
int output_width = NUM2INT(output_width_val);
|
||||
int output_height = NUM2INT(output_height_val);
|
||||
int output_quality = NUM2INT(output_quality_val);
|
||||
|
||||
FILE *read_file = fopen(read_path, "rb");
|
||||
if(read_file == NULL)
|
||||
rb_raise(rb_eIOError, "can't open %s\n", read_path);
|
||||
|
||||
FILE *write_file = fopen(write_path, "wb");
|
||||
if(write_file == NULL)
|
||||
{
|
||||
fclose(read_file);
|
||||
rb_raise(rb_eIOError, "can't open %s\n", write_path);
|
||||
}
|
||||
|
||||
bool ret = false;
|
||||
char error[1024];
|
||||
JPEGCompressor *Compressor = NULL;
|
||||
Resizer *resizer = NULL;
|
||||
Reader *Reader = NULL;
|
||||
|
||||
if (!strcmp(file_ext, "jpg") || !strcmp(file_ext, "jpeg"))
|
||||
Reader = new JPEG;
|
||||
else if (!strcmp(file_ext, "gif"))
|
||||
Reader = new GIF;
|
||||
else if (!strcmp(file_ext, "png"))
|
||||
Reader = new PNG;
|
||||
else
|
||||
{
|
||||
strcpy(error, "unknown filetype");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
Compressor = new JPEGCompressor(write_file);
|
||||
if(Compressor == NULL)
|
||||
{
|
||||
strcpy(error, "out of memory");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
resizer = new Resizer(Compressor);
|
||||
if(resizer == NULL || Reader == NULL)
|
||||
{
|
||||
strcpy(error, "out of memory");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
resizer->SetDest(output_width, output_height, output_quality);
|
||||
ret = Reader->Read(read_file, resizer, error);
|
||||
|
||||
cleanup:
|
||||
delete Reader;
|
||||
delete resizer;
|
||||
delete Compressor;
|
||||
|
||||
fclose(read_file);
|
||||
fclose(write_file);
|
||||
|
||||
if(!ret)
|
||||
rb_raise(rb_eException, "%s", error);
|
||||
|
||||
return INT2FIX(0);
|
||||
}
|
||||
|
||||
extern "C" void Init_danbooru_image_resizer() {
|
||||
danbooru_module = rb_define_module("Danbooru");
|
||||
rb_define_module_function(danbooru_module, "resize_image", (VALUE(*)(...))danbooru_resize_image, 6);
|
||||
}
|
||||
29
lib/danbooru_image_resizer/danbooru_image_resizer.rb
Normal file
29
lib/danbooru_image_resizer/danbooru_image_resizer.rb
Normal file
@@ -0,0 +1,29 @@
|
||||
require 'danbooru_image_resizer/danbooru_image_resizer.so'
|
||||
|
||||
module Danbooru
|
||||
def resize(file_ext, read_path, write_path, output_size, output_quality)
|
||||
Danbooru.resize_image(file_ext, read_path, write_path, output_size[:width], output_size[:height], output_quality)
|
||||
end
|
||||
|
||||
def reduce_to(size, max_size)
|
||||
returning size.dup do |new_size|
|
||||
if new_size[:width] > max_size[:width]
|
||||
scale = max_size[:width].to_f / new_size[:width].to_f
|
||||
new_size[:width] = new_size[:width] * scale
|
||||
new_size[:height] = new_size[:height] * scale
|
||||
end
|
||||
|
||||
if max_size[:height] && (new_size[:height] > max_size[:height])
|
||||
scale = max_size[:height].to_f / new_size[:height].to_f
|
||||
new_size[:width] = new_size[:width] * scale
|
||||
new_size[:height] = new_size[:height] * scale
|
||||
end
|
||||
|
||||
new_size[:width] = new_size[:width].to_i
|
||||
new_size[:height] = new_size[:height].to_i
|
||||
end
|
||||
end
|
||||
|
||||
module_function :resize
|
||||
module_function :reduce_to
|
||||
end
|
||||
26
lib/danbooru_image_resizer/extconf.rb
Normal file
26
lib/danbooru_image_resizer/extconf.rb
Normal file
@@ -0,0 +1,26 @@
|
||||
#!/bin/env ruby
|
||||
|
||||
require 'mkmf'
|
||||
|
||||
CONFIG['CC'] = "g++"
|
||||
CONFIG['LDSHARED'] = CONFIG['LDSHARED'].sub(/^cc /,'g++ ') # otherwise we would not link with the C++ runtime
|
||||
|
||||
dir_config("gd")
|
||||
dir_config("jpeg")
|
||||
dir_config("png")
|
||||
|
||||
have_header("gd.h")
|
||||
|
||||
have_library("gd")
|
||||
have_library("jpeg")
|
||||
have_library("png")
|
||||
|
||||
have_func("gdImageCreateFromGif", "gd.h")
|
||||
have_func("gdImageJpeg", "gd.h")
|
||||
have_func("jpeg_set_quality", ["stdlib.h", "stdio.h", "jpeglib-extern.h"])
|
||||
have_func("png_set_expand_gray_1_2_4_to_8", "png.h")
|
||||
|
||||
with_cflags("-O2 -fno-exceptions -Wall") {true}
|
||||
#with_cflags("-O0 -g -fno-exceptions -Wall") {true}
|
||||
|
||||
create_makefile("danbooru_image_resizer")
|
||||
16
lib/danbooru_image_resizer/jpeglib-extern.h
Normal file
16
lib/danbooru_image_resizer/jpeglib-extern.h
Normal file
@@ -0,0 +1,16 @@
|
||||
// Needed for OS X
|
||||
|
||||
#ifndef JPEGLIB_EXTERN_H
|
||||
#define JPEGLIB_EXTERN_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <jpeglib.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user