Commit 88a1cb11 authored by Slava Monich's avatar Slava Monich

[mms-lib] Implemented libjpeg-based scaling

Now ImageMagick is only used for scaling non-JPEG images.
parent e4e1f4db
*~
build
mms-engine/mms-engine.pro.user
mms-engine/build
mms-lib/mms-lib.pro.user
mms-lib/build
mms-lib/test/coverage/full.gcov
mms-lib/test/coverage/mms-lib.gcov
mms-lib/test/coverage/results
mms-lib/test/media_type/build
mms-lib/test/mms_codec/build
mms-lib/test/mms_lib_test.ncb
mms-lib/test/mms_lib_test.opt
mms-lib/test/read_report/build
mms-lib/test/retrieve/build
mms-lib/test/retrieve_cancel/build
mms-lib/test/retrieve_no_proxy/build
mms-ofono/mms-ofono.pro.user
mms-ofono/build
mms-handler-dbus/mms-handler-dbus.pro.user
mms-handler-dbus/build
mms-handler-dbus/test/mms_handler_dbus_server/build
mms-handler-dbus/test/mms_handler_dbus_server/test_mms_handler_dbus_server.pro.user
mms-handler-dbus/test/mms_handler_dbus_client/build
mms-dump/build
mms-dump/mms_dump.ncb
mms-dump/mms_dump.opt
mms-send/build
......@@ -90,7 +90,7 @@ endif
DEBUG_CFLAGS = $(DEBUG_FLAGS) $(DEBUG_DEFS) $(CFLAGS)
RELEASE_CFLAGS = $(RELEASE_FLAGS) $(RELEASE_DEFS) $(CFLAGS)
LIBS = $(shell pkg-config --libs $(LIB_PKGS)) -lmagic
LIBS = $(shell pkg-config --libs $(LIB_PKGS)) -lmagic -ljpeg
DEBUG_LIBS = \
$(MMS_OFONO_DEBUG_LIB) \
$(MMS_HANDLER_DEBUG_LIB) \
......
......@@ -150,6 +150,7 @@ mms_app_parse_options(
GError* error = NULL;
gboolean session_bus = FALSE;
gint size_limit_kb = opt->config.size_limit/1024;
gdouble megapixels = opt->config.max_pixels / 1000000.0;
char* root_dir_help = g_strdup_printf(
"Root directory for MMS files [%s]",
opt->config.root_dir);
......@@ -162,6 +163,9 @@ mms_app_parse_options(
char* size_limit_help = g_strdup_printf(
"Maximum size for outgoing messages [%d]",
size_limit_kb);
char* megapixels_help = g_strdup_printf(
"Maximum pixel count for outgoing images [%.1f]",
megapixels);
char* description = mms_log_description(mms_app_log_modules,
G_N_ELEMENTS(mms_app_log_modules));
......@@ -177,6 +181,8 @@ mms_app_parse_options(
&opt->config.idle_secs, idle_secs_help, "SEC" },
{ "size-limit", 's', 0, G_OPTION_ARG_INT,
&size_limit_kb, size_limit_help, "KB" },
{ "pix-limit", 'p', 0, G_OPTION_ARG_DOUBLE,
&megapixels, megapixels_help, "MPIX" },
{ "keep-running", 'k', 0, G_OPTION_ARG_NONE, &opt->keep_running,
"Keep running after everything is done", NULL },
{ "keep-temp-files", 't', 0, G_OPTION_ARG_NONE,
......@@ -189,7 +195,7 @@ mms_app_parse_options(
mms_app_option_verbose, "Be verbose (equivalent to -l=verbose)",
NULL },
{ "log-output", 'o', 0, G_OPTION_ARG_CALLBACK, mms_app_option_logtype,
"Log output [stdout]", "<stdout|syslog|glib>" },
"Log output (stdout|syslog|glib) [stdout]", "TYPE" },
{ "log-level", 'l', 0, G_OPTION_ARG_CALLBACK, mms_app_option_loglevel,
"Set log level (repeatable)", "[MODULE:]LEVEL" },
{ NULL }
......@@ -204,11 +210,21 @@ mms_app_parse_options(
g_free(retry_secs_help);
g_free(idle_secs_help);
g_free(size_limit_help);
g_free(megapixels_help);
g_free(description);
if (ok && size_limit_kb >= 0) {
if (ok) {
MMS_INFO("Starting");
opt->config.size_limit = size_limit_kb * 1024;
if (size_limit_kb >= 0) {
opt->config.size_limit = size_limit_kb * 1024;
} else {
opt->config.size_limit = 0;
}
if (megapixels >= 0) {
opt->config.max_pixels = (int)(megapixels*1000)*1000;
} else {
opt->config.max_pixels = 0;
}
if (opt->dir) opt->config.root_dir = opt->dir;
if (session_bus) {
MMS_DEBUG("Attaching to session bus");
......
......@@ -35,7 +35,7 @@ CONFIG(debug, debug|release) {
LIBS += $$MMS_LIB_DIR/build/release/libmms-lib.a
}
LIBS += -lmagic
LIBS += -lmagic -ljpeg
MMS_ENGINE_DBUS_XML = $$DBUS_INTERFACE_DIR/org.nemomobile.MmsEngine.xml
MMS_ENGINE_DBUS_H = org.nemomobile.MmsEngine.h
......
......@@ -15,12 +15,13 @@ all: debug release
# Sources
#
SRC = mms_attachment.c mms_attachment_image.c mms_codec.c mms_connection.c \
mms_connman.c mms_dispatcher.c mms_error.c mms_handler.c mms_lib_util.c \
mms_file_util.c mms_log.c mms_message.c mms_task.c mms_task_ack.c \
mms_task_decode.c mms_task_encode.c mms_task_http.c mms_task_notification.c \
mms_task_notifyresp.c mms_task_publish.c mms_task_read.c \
mms_task_retrieve.c mms_task_send.c mms_util.c
SRC = mms_attachment.c mms_attachment_image.c mms_attachment_jpeg.c \
mms_codec.c mms_connection.c mms_connman.c mms_dispatcher.c mms_error.c \
mms_handler.c mms_lib_util.c mms_file_util.c mms_log.c mms_message.c \
mms_task.c mms_task_ack.c mms_task_decode.c mms_task_encode.c \
mms_task_http.c mms_task_notification.c mms_task_notifyresp.c \
mms_task_publish.c mms_task_read.c mms_task_retrieve.c mms_task_send.c \
mms_util.c
#
# Directories
......
......@@ -50,6 +50,7 @@ typedef struct mms_config {
int retry_secs; /* Retry timeout in seconds */
int idle_secs; /* Idle timeout */
gsize size_limit; /* Maximum size of m-Send.req PDU */
guint max_pixels; /* Pixel limit for outbound images */
gboolean keep_temp_files; /* Keep temporary files around */
gboolean attic_enabled; /* Keep unrecognized push message in attic */
gboolean send_dr; /* Allow sending delivery reports */
......
......@@ -19,6 +19,7 @@ CONFIG(debug, debug|release) {
SOURCES += \
src/mms_attachment.c \
src/mms_attachment_image.c \
src/mms_attachment_jpeg.c \
src/mms_codec.c \
src/mms_connection.c \
src/mms_connman.c \
......@@ -44,6 +45,7 @@ SOURCES += \
HEADERS += \
src/mms_attachment.h \
src/mms_attachment_image.h \
src/mms_codec.h \
src/mms_error.h \
src/mms_file_util.h \
......
......@@ -50,9 +50,10 @@ mms_attachment_finalize(
GObject* object)
{
MMSAttachment* at = MMS_ATTACHMENT(object);
MMS_VERBOSE_("%p", at);
g_mapped_file_unref(at->map);
if (!at->config->keep_temp_files &&
!(at->flags & MMS_ATTACHMENT_DONT_DELETE_FILES)) {
!(at->flags & MMS_ATTACHMENT_KEEP_FILES)) {
char* dir = g_path_get_dirname(at->original_file);
remove(at->original_file);
rmdir(dir);
......@@ -76,9 +77,9 @@ mms_attachment_class_init(
static
void
mms_attachment_init(
MMSAttachment* attachment)
MMSAttachment* at)
{
MMS_VERBOSE_("%p", attachment);
MMS_VERBOSE_("%p", at);
}
static
......@@ -210,6 +211,7 @@ mms_attachment_new(
if (map) {
unsigned int flags = 0;
char* content_type = NULL;
GType type;
MMSAttachment* at;
if (info->content_type) {
......@@ -274,9 +276,16 @@ mms_attachment_new(
}
MMS_DEBUG("%s: %s", path, content_type);
at = g_object_new(g_str_has_prefix(content_type, "image/") ?
MMS_TYPE_ATTACHMENT_IMAGE : MMS_TYPE_ATTACHMENT, NULL);
if (!strcmp(content_type, "image/jpeg")) {
type = MMS_TYPE_ATTACHMENT_JPEG;
} else if (g_str_has_prefix(content_type, "image/")) {
type = MMS_TYPE_ATTACHMENT_IMAGE;
} else {
type = MMS_TYPE_ATTACHMENT;
}
at = g_object_new(type, NULL);
at->config = config;
at->map = map;
at->flags |= flags;
......
......@@ -29,9 +29,9 @@ struct _mms_attachment {
GMappedFile* map; /* Mapped attachment file */
unsigned int flags; /* Flags: */
#define MMS_ATTACHMENT_SMIL (0x01)
#define MMS_ATTACHMENT_DONT_DELETE_FILES (0x02)
#define MMS_ATTACHMENT_RESIZABLE (0x04)
#define MMS_ATTACHMENT_SMIL (0x01)
#define MMS_ATTACHMENT_KEEP_FILES (0x02)
#define MMS_ATTACHMENT_RESIZABLE (0x04)
};
......@@ -43,10 +43,10 @@ typedef struct mms_attachment_class {
GType mms_attachment_get_type(void);
GType mms_attachment_image_get_type(void);
#define MMS_TYPE_ATTACHMENT (mms_attachment_get_type())
#define MMS_TYPE_ATTACHMENT_IMAGE (mms_attachment_image_get_type())
#define MMS_ATTACHMENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \
MMS_TYPE_ATTACHMENT, MMSAttachmentClass))
GType mms_attachment_jpeg_get_type(void);
#define MMS_TYPE_ATTACHMENT (mms_attachment_get_type())
#define MMS_TYPE_ATTACHMENT_IMAGE (mms_attachment_image_get_type())
#define MMS_TYPE_ATTACHMENT_JPEG (mms_attachment_jpeg_get_type())
MMSAttachment*
mms_attachment_new(
......
This diff is collapsed.
/*
* Copyright (C) 2013-2014 Jolla Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*
*/
#ifndef JOLLA_MMS_ATTACHMENT_IMAGE_H
#define JOLLA_MMS_ATTACHMENT_IMAGE_H
#include "mms_attachment.h"
typedef struct mms_attachment_image_size {
unsigned int width;
unsigned int height;
} MMSAttachmentImageSize;
typedef struct mms_attachment_image_resize {
MMSAttachmentImageSize image;
MMSAttachmentImageSize in;
MMSAttachmentImageSize out;
} MMSAttachmentImageResize;
typedef struct mms_attachment_image {
MMSAttachment attachment;
int resize_step;
char* resized;
} MMSAttachmentImage;
typedef struct mms_attachment_image_class {
MMSAttachmentClass attachment;
/* Creates the resize context, sets image size */
MMSAttachmentImageResize*
(*fn_resize_new)(
MMSAttachmentImage* image,
const char* file);
/* Prepares the resize context for writing, sets input size */
gboolean
(*fn_resize_prepare)(
MMSAttachmentImageResize* resize,
const char* file);
/* Reads the next scanline in RGB24 format */
gboolean
(*fn_resize_read_line)(
MMSAttachmentImageResize* resize,
unsigned char* rgb24);
/* Writes the next scanline in RGB24 format */
gboolean
(*fn_resize_write_line)(
MMSAttachmentImageResize* resize,
const unsigned char* rgb24);
/* Finishes resizing */
void (*fn_resize_finish)(
MMSAttachmentImageResize* resize);
/* Frees the resize context */
void (*fn_resize_free)(
MMSAttachmentImageResize* resize);
} MMSAttachmentImageClass;
#endif /* JOLLA_MMS_ATTACHMENT_IMAGE_H */
/*
* Local Variables:
* mode: C
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/
/*
* Copyright (C) 2013-2014 Jolla Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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.
*
*/
#include "mms_attachment_image.h"
#include "mms_file_util.h"
#include <jpeglib.h>
#include <jerror.h>
#include <setjmp.h>
/* Logging */
#define MMS_LOG_MODULE_NAME mms_attachment_log
#include "mms_lib_log.h"
typedef MMSAttachmentImageClass MMSAttachmentJpegClass;
typedef MMSAttachmentImage MMSAttachmentJpeg;
G_DEFINE_TYPE(MMSAttachmentJpeg, mms_attachment_jpeg, \
MMS_TYPE_ATTACHMENT_IMAGE);
#define MMS_ATTACHMENT_JPEG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
MMS_TYPE_ATTACHMENT_JPEG, MMSAttachmentJpeg))
typedef struct mms_attachment_jpeg_error {
struct jpeg_error_mgr pub;
jmp_buf setjmp_buf;
} MMSAttachmentJpegError;
typedef struct mms_attachment_jpeg_resize {
MMSAttachmentImageResize pub;
MMSAttachmentJpegError err;
struct jpeg_decompress_struct decomp;
struct jpeg_compress_struct comp;
FILE* in;
FILE* out;
} MMSAttachmentJpegResize;
static inline MMSAttachmentJpegResize*
mms_attachment_jpeg_resize_cast(MMSAttachmentImageResize* resize)
{ return MMS_CAST(resize, MMSAttachmentJpegResize, pub); }
static
void
mms_attachment_jpeg_error_log(
int level,
j_common_ptr cinfo)
{
char* buf = g_malloc(JMSG_LENGTH_MAX);
buf[0] = 0;
cinfo->err->format_message(cinfo, buf);
buf[JMSG_LENGTH_MAX-1] = 0;
mms_log(MMS_LOG_MODULE_CURRENT, level, "%s", buf);
g_free(buf);
}
static
void
mms_attachment_jpeg_error_exit(
j_common_ptr cinfo)
{
MMSAttachmentJpegError* err = (MMSAttachmentJpegError*)cinfo->err;
mms_attachment_jpeg_error_log(MMS_LOGLEVEL_WARN, cinfo);
longjmp(err->setjmp_buf, 1);
}
static
void
mms_attachment_jpeg_error_output(
j_common_ptr cinfo)
{
mms_attachment_jpeg_error_log(MMS_LOGLEVEL_DEBUG, cinfo);
}
static
MMSAttachmentImageResize*
mms_attachment_jpeg_resize_new(
MMSAttachmentImage* image,
const char* file)
{
MMSAttachmentJpegResize* jpeg = g_new0(MMSAttachmentJpegResize, 1);
jpeg->in = fopen(file, "rb");
if (jpeg->in) {
jpeg->decomp.err = jpeg_std_error(&jpeg->err.pub);
jpeg->err.pub.error_exit = mms_attachment_jpeg_error_exit;
jpeg->err.pub.output_message = mms_attachment_jpeg_error_output;
if (!setjmp(jpeg->err.setjmp_buf)) {
int i;
jpeg_create_decompress(&jpeg->decomp);
jpeg_save_markers(&jpeg->decomp, JPEG_COM, 0xFFFF);
for (i=0; i<16; i++) {
jpeg_save_markers(&jpeg->decomp, JPEG_APP0+i, 0xFFFF);
}
jpeg_stdio_src(&jpeg->decomp, jpeg->in);
jpeg_read_header(&jpeg->decomp, TRUE);
jpeg->pub.image.width = jpeg->decomp.image_width;
jpeg->pub.image.height = jpeg->decomp.image_height;
jpeg->pub.in = jpeg->pub.image;
return &jpeg->pub;
}
jpeg_destroy_decompress(&jpeg->decomp);
fclose(jpeg->in);
}
g_free(jpeg);
return NULL;
}
static
gboolean
mms_attachment_jpeg_resize_prepare(
MMSAttachmentImageResize* resize,
const char* file)
{
MMSAttachmentJpegResize* jpeg = mms_attachment_jpeg_resize_cast(resize);
jpeg->out = fopen(file, "wb");
if (jpeg->out) {
jpeg->comp.err = &jpeg->err.pub;
if (!setjmp(jpeg->err.setjmp_buf)) {
jpeg_saved_marker_ptr marker;
jpeg_create_compress(&jpeg->comp);
jpeg->decomp.scale_num = resize->in.width;
jpeg->decomp.scale_denom = resize->image.width;
jpeg->decomp.out_color_space = JCS_RGB;
jpeg_start_decompress(&jpeg->decomp);
if (jpeg->decomp.output_width == resize->in.width &&
jpeg->decomp.output_height == resize->in.height) {
jpeg->comp.image_width = resize->out.width;
jpeg->comp.image_height = resize->out.height;
jpeg->comp.input_components = 3;
jpeg->comp.in_color_space = JCS_RGB;
jpeg_stdio_dest(&jpeg->comp, jpeg->out);
jpeg_set_defaults(&jpeg->comp);
jpeg_set_quality(&jpeg->comp, 90, TRUE);
jpeg->comp.write_JFIF_header = jpeg->decomp.saw_JFIF_marker;
jpeg_start_compress(&jpeg->comp, TRUE);
for (marker = jpeg->decomp.marker_list;
marker != NULL;
marker = marker->next) {
/* Avoid duplicating markers */
if (jpeg->comp.write_JFIF_header &&
marker->marker == JPEG_APP0 &&
marker->data_length >= 5 &&
memcmp("JFIF", marker->data, 5) == 0) {
continue;
}
if (jpeg->comp.write_Adobe_marker &&
marker->marker == JPEG_APP0+14 &&
marker->data_length >= 5 &&
memcmp("Adobe", marker->data, 5) == 0) {
continue;
}
jpeg_write_marker(&jpeg->comp, marker->marker,
marker->data, marker->data_length);
}
return TRUE;
}
}
}
return FALSE;
}
static
gboolean
mms_attachment_jpeg_read_line(
MMSAttachmentImageResize* resize,
unsigned char* rgb24)
{
MMSAttachmentJpegResize* jpeg = mms_attachment_jpeg_resize_cast(resize);
if (!setjmp(jpeg->err.setjmp_buf)) {
JSAMPROW row = rgb24;
jpeg_read_scanlines(&jpeg->decomp, &row, 1);
return TRUE;
}
return FALSE;
}
static
gboolean
mms_attachment_jpeg_write_line(
MMSAttachmentImageResize* resize,
const unsigned char* rgb24)
{
MMSAttachmentJpegResize* jpeg = mms_attachment_jpeg_resize_cast(resize);
if (!setjmp(jpeg->err.setjmp_buf)) {
JSAMPROW row = (void*)rgb24;
jpeg_write_scanlines(&jpeg->comp, &row, 1);
return TRUE;
}
return FALSE;
}
static
void
mms_attachment_jpeg_resize_finish(
MMSAttachmentImageResize* resize)
{
MMSAttachmentJpegResize* jpeg = mms_attachment_jpeg_resize_cast(resize);
if (!setjmp(jpeg->err.setjmp_buf)) {
jpeg_finish_compress(&jpeg->comp);
jpeg_finish_decompress(&jpeg->decomp);
}
}
static
void
mms_attachment_jpeg_resize_free(
MMSAttachmentImageResize* resize)
{
MMSAttachmentJpegResize* jpeg = mms_attachment_jpeg_resize_cast(resize);
jpeg_destroy_compress(&jpeg->comp);
jpeg_destroy_decompress(&jpeg->decomp);
fclose(jpeg->in);
fclose(jpeg->out);
g_free(jpeg);
}
static
void
mms_attachment_jpeg_class_init(
MMSAttachmentJpegClass* klass)
{
klass->fn_resize_new = mms_attachment_jpeg_resize_new;
klass->fn_resize_prepare = mms_attachment_jpeg_resize_prepare;
klass->fn_resize_read_line = mms_attachment_jpeg_read_line;
klass->fn_resize_write_line = mms_attachment_jpeg_write_line;
klass->fn_resize_finish = mms_attachment_jpeg_resize_finish;
klass->fn_resize_free = mms_attachment_jpeg_resize_free;
}
static
void
mms_attachment_jpeg_init(
MMSAttachmentJpeg* jpeg)
{
jpeg->attachment.flags |= MMS_ATTACHMENT_RESIZABLE;
}
/*
* Local Variables:
* mode: C
* c-basic-offset: 4
* indent-tabs-mode: nil
* End:
*/
......@@ -27,6 +27,7 @@
#define MMS_DEFAULT_RETRY_SECS (15)
#define MMS_DEFAULT_IDLE_SECS (20)
#define MMS_DEFAULT_SIZE_LIMIT (300*1024)
#define MMS_DEFAULT_MAX_PIXELS (3000000)
/**
* MMS error domain
......@@ -78,6 +79,7 @@ mms_lib_default_config(
config->retry_secs = MMS_DEFAULT_RETRY_SECS;
config->idle_secs = MMS_DEFAULT_IDLE_SECS;
config->size_limit = MMS_DEFAULT_SIZE_LIMIT;
config->max_pixels = MMS_DEFAULT_MAX_PIXELS;
config->keep_temp_files = FALSE;
config->attic_enabled = FALSE;
config->send_dr = TRUE;
......
......@@ -18,8 +18,8 @@ endif
#
# Required packages
#
LIB_PKGS = libwspcodec libsoup-2.4 glib-2.0 ImageMagick
PKGS = $(LIB_PKGS)
LIB_PKGS += libwspcodec libsoup-2.4 glib-2.0 ImageMagick
PKGS += $(LIB_PKGS)
#
# Default target
......
......@@ -3,7 +3,7 @@
# This script requires lcov to be installed
#
TESTS="media_type mms_codec read_report retrieve retrieve_cancel retrieve_no_proxy"
TESTS="media_type mms_codec read_report resize retrieve retrieve_cancel retrieve_no_proxy"
FLAVOR="release"
pushd `dirname $0` > /dev/null
......
# -*- Mode: makefile-gmake -*-
EXE = test_resize
SRC = test_resize.c
LIB_PKGS = libexif libpng
include ../common/Makefile
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment