Skip to content

Commit

Permalink
Add libjpeg port (#8361)
Browse files Browse the repository at this point in the history
  • Loading branch information
msabramo authored and kripken committed Mar 29, 2019
1 parent dbe8149 commit df9f7f1
Show file tree
Hide file tree
Showing 6 changed files with 345 additions and 1 deletion.
3 changes: 3 additions & 0 deletions embuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
'freetype',
'harfbuzz',
'icu',
'libjpeg',
'libpng',
'ogg',
'regal',
Expand Down Expand Up @@ -326,6 +327,8 @@ def is_flag(arg):
build_port('vorbis', libname('libvorbis'), ['-s', 'USE_VORBIS=1'])
elif what == 'ogg':
build_port('ogg', libname('libogg'), ['-s', 'USE_OGG=1'])
elif what == 'libjpeg':
build_port('libjpeg', libname('libjpeg'), ['-s', 'USE_LIBJPEG=1'])
elif what == 'libpng':
build_port('libpng', libname('libpng'), ['-s', 'USE_ZLIB=1', '-s', 'USE_LIBPNG=1'])
elif what == 'sdl2':
Expand Down
3 changes: 3 additions & 0 deletions src/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -1083,6 +1083,9 @@ var USE_ZLIB = 0;
// 1 = use bzip2 from emscripten-ports
var USE_BZIP2 = 0;

// 1 = use libjpeg from emscripten-ports
var USE_LIBJPEG = 0;

// 1 = use libpng from emscripten-ports
var USE_LIBPNG = 0;

Expand Down
211 changes: 211 additions & 0 deletions tests/jpeg_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
// memdjpeg - A super simple example of how to decode a jpeg in memory
// Kenneth Finnegan, 2012
// blog.thelifeofkenneth.com
//
// After installing jpeglib, compile with:
// cc memdjpeg.c -ljpeg -o memdjpeg
//
// Run with:
// ./memdjpeg filename.jpg
//
// Version Date Time By
// ------- ---------- ----- ---------
// 0.01 2012-07-09 11:18 Kenneth Finnegan
//

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>

#include <jpeglib.h>


int main (int argc, char *argv[]) {
int rc, i, j;

if (argc != 2) {
fprintf(stderr, "USAGE: %s filename.jpg\n", argv[0]);
exit(EXIT_FAILURE);
}

// SSS EEEEEEE TTTTTTT U U PPPP
// SS SS E T U U P PP
// S E T U U P PP
// SS E T U U P PP
// SSS EEEE T U U PPPP
// SS E T U U P
// S E T U U P
// SS SS E T U U P
// SSS EEEEEEE T UUU P

// Variables for the source jpg
struct stat file_info;
unsigned long jpg_size;
unsigned char *jpg_buffer;

// Variables for the decompressor itself
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;

// Variables for the output buffer, and how long each row is
unsigned long bmp_size;
unsigned char *bmp_buffer;
int row_stride, width, height, pixel_size;


// Load the jpeg data from a file into a memory buffer for
// the purpose of this demonstration.
// Normally, if it's a file, you'd use jpeg_stdio_src, but just
// imagine that this was instead being downloaded from the Internet
// or otherwise not coming from disk
rc = stat(argv[1], &file_info);
if (rc) {
fprintf(stderr, "FAILED to stat source jpg\n");
exit(EXIT_FAILURE);
}
jpg_size = file_info.st_size;
jpg_buffer = (unsigned char*) malloc(jpg_size + 100);

int fd = open(argv[1], O_RDONLY);
i = 0;
while (i < jpg_size) {
rc = read(fd, jpg_buffer + i, jpg_size - i);
printf("Input: Read %d/%lu bytes\n", rc, jpg_size-i);
i += rc;
}
close(fd);

// SSS TTTTTTT A RRRR TTTTTTT
// SS SS T A A R RR T
// S T A A R RR T
// SS T A A R RR T
// SSS T AAAAAAA RRRR T
// SS T A A R RR T
// S T A A R R T
// SS SS T A A R R T
// SSS T A A R R T

printf("Proc: Create Decompress struct\n");
// Allocate a new decompress struct, with the default error handler.
// The default error handler will exit() on pretty much any issue,
// so it's likely you'll want to replace it or supplement it with
// your own.
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);


printf("Proc: Set memory buffer as source\n");
// Configure this decompressor to read its data from a memory
// buffer starting at unsigned char *jpg_buffer, which is jpg_size
// long, and which must contain a complete jpg already.
//
// If you need something fancier than this, you must write your
// own data source manager, which shouldn't be too hard if you know
// what it is you need it to do. See jpeg-8d/jdatasrc.c for the
// implementation of the standard jpeg_mem_src and jpeg_stdio_src
// managers as examples to work from.
jpeg_mem_src(&cinfo, jpg_buffer, jpg_size);


printf("Proc: Read the JPEG header\n");
// Have the decompressor scan the jpeg header. This won't populate
// the cinfo struct output fields, but will indicate if the
// jpeg is valid.
rc = jpeg_read_header(&cinfo, TRUE);

if (rc != 1) {
fprintf(stderr, "File does not seem to be a normal JPEG\n");
exit(EXIT_FAILURE);
}

printf("Proc: Initiate JPEG decompression\n");
// By calling jpeg_start_decompress, you populate cinfo
// and can then allocate your output bitmap buffers for
// each scanline.
jpeg_start_decompress(&cinfo);

width = cinfo.output_width;
height = cinfo.output_height;
pixel_size = cinfo.output_components;

printf("Proc: Image is %d by %d with %d components\n",
width, height, pixel_size);

bmp_size = width * height * pixel_size;
bmp_buffer = (unsigned char*) malloc(bmp_size);

// The row_stride is the total number of bytes it takes to store an
// entire scanline (row).
row_stride = width * pixel_size;


printf("Proc: Start reading scanlines\n");
//
// Now that you have the decompressor entirely configured, it's time
// to read out all of the scanlines of the jpeg.
//
// By default, scanlines will come out in RGBRGBRGB... order,
// but this can be changed by setting cinfo.out_color_space
//
// jpeg_read_scanlines takes an array of buffers, one for each scanline.
// Even if you give it a complete set of buffers for the whole image,
// it will only ever decompress a few lines at a time. For best
// performance, you should pass it an array with cinfo.rec_outbuf_height
// scanline buffers. rec_outbuf_height is typically 1, 2, or 4, and
// at the default high quality decompression setting is always 1.
while (cinfo.output_scanline < cinfo.output_height) {
unsigned char *buffer_array[1];
buffer_array[0] = bmp_buffer + \
(cinfo.output_scanline) * row_stride;

jpeg_read_scanlines(&cinfo, buffer_array, 1);

}
printf("Proc: Done reading scanlines\n");


// Once done reading *all* scanlines, release all internal buffers,
// etc by calling jpeg_finish_decompress. This lets you go back and
// reuse the same cinfo object with the same settings, if you
// want to decompress several jpegs in a row.
//
// If you didn't read all the scanlines, but want to stop early,
// you instead need to call jpeg_abort_decompress(&cinfo)
jpeg_finish_decompress(&cinfo);

// At this point, optionally go back and either load a new jpg into
// the jpg_buffer, or define a new jpeg_mem_src, and then start
// another decompress operation.

// Once you're really really done, destroy the object to free everything
jpeg_destroy_decompress(&cinfo);
// And free the input buffer
free(jpg_buffer);

// DDDD OOO N N EEEEEEE
// D DDD O O NN N E
// D DD O O N N N E
// D D O O N N N E
// D D O O N N N EEEE
// D D O O N N N E
// D DD O O N N N E
// D DDD O O N NN E
// DDDD OOO N N EEEEEEE

// Write the decompressed bitmap out to a ppm file, just to make sure
// it worked.
fd = open("output.ppm", O_CREAT | O_WRONLY, 0666);
char buf[1024];

rc = sprintf(buf, "P6 %d %d 255\n", width, height);
write(fd, buf, rc); // Write the PPM image header before data
write(fd, bmp_buffer, bmp_size); // Write out all RGB pixel data

close(fd);
free(bmp_buffer);

printf("End of decompression\n");
return EXIT_SUCCESS;
}
5 changes: 5 additions & 0 deletions tests/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -1827,6 +1827,11 @@ def test_libpng(self):
Building.emcc(path_from_root('tests', 'pngtest.c'), ['--embed-file', 'pngtest.png', '-s', 'USE_ZLIB=1', '-s', 'USE_LIBPNG=1'], output_filename='a.out.js')
self.assertContained('TESTS PASSED', run_process(JS_ENGINES[0] + ['a.out.js'], stdout=PIPE, stderr=PIPE).stdout)

def test_libjpeg(self):
shutil.copyfile(path_from_root('tests', 'screenshot.jpg'), 'screenshot.jpg')
Building.emcc(path_from_root('tests', 'jpeg_test.c'), ['--embed-file', 'screenshot.jpg', '-s', 'USE_LIBJPEG=1'], output_filename='a.out.js')
self.assertContained('Image is 600 by 450 with 3 components', run_process(JS_ENGINES[0] + ['a.out.js', 'screenshot.jpg'], stdout=PIPE, stderr=PIPE).stdout)

def test_bullet(self):
Building.emcc(path_from_root('tests', 'bullet_hello_world.cpp'), ['-s', 'USE_BULLET=1'], output_filename='a.out.js')
self.assertContained('BULLET RUNNING', run_process(JS_ENGINES[0] + ['a.out.js'], stdout=PIPE, stderr=PIPE).stdout)
Expand Down
3 changes: 2 additions & 1 deletion tools/ports/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# University of Illinois/NCSA Open Source License. Both these licenses can be
# found in the LICENSE file.

from . import binaryen, bullet, cocos2d, freetype, harfbuzz, icu, libpng
from . import binaryen, bullet, cocos2d, freetype, harfbuzz, icu, libjpeg, libpng
from . import ogg, regal, sdl2, sdl2_gfx, sdl2_image, sdl2_mixer, sdl2_ttf
from . import sdl2_net, vorbis, zlib, bzip2

Expand All @@ -12,6 +12,7 @@
icu,
zlib,
bzip2,
libjpeg,
libpng,
sdl2,
sdl2_image,
Expand Down
121 changes: 121 additions & 0 deletions tools/ports/libjpeg.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# Copyright 2014 The Emscripten Authors. All rights reserved.
# Emscripten is available under two separate licenses, the MIT license and the
# University of Illinois/NCSA Open Source License. Both these licenses can be
# found in the LICENSE file.

import os
import shutil
import logging

VERSION = '9c'


def get(ports, settings, shared):
if settings.USE_LIBJPEG != 1:
return []

ports.fetch_project('libjpeg', 'https://dl.bintray.com/homebrew/mirror/jpeg-9c.tar.gz', 'jpeg-9c')

libname = ports.get_lib_name('libjpeg')

def create():
logging.info('building port: libjpeg')

source_path = os.path.join(ports.get_dir(), 'libjpeg', 'jpeg-9c')
dest_path = os.path.join(shared.Cache.get_path('ports-builds'), 'libjpeg')

shutil.rmtree(dest_path, ignore_errors=True)
shutil.copytree(source_path, dest_path)

open(os.path.join(dest_path, 'jconfig.h'), 'w').write(jconfig_h)

final = os.path.join(ports.get_build_dir(), 'libjpeg', libname)
ports.build_port(
dest_path, final,
exclude_files=[
'ansi2knr.c', 'cjpeg.c', 'ckconfig.c', 'djpeg.c', 'example.c',
'jmemansi.c', 'jmemdos.c', 'jmemmac.c', 'jmemname.c',
'jpegtran.c', 'rdjpgcom.c', 'wrjpgcom.c',
]
)
return final

return [shared.Cache.get(libname, create, what='port')]


def clear(ports, shared):
shared.Cache.erase_file(ports.get_lib_name('libjpeg'))


def process_args(ports, args, settings, shared):
if settings.USE_LIBJPEG == 1:
get(ports, settings, shared)
args += ['-Xclang', '-isystem' + os.path.join(shared.Cache.get_path('ports-builds'), 'libjpeg')]
return args


def show():
return 'libjpeg (USE_LIBJPEG=1; BSD license)'


jconfig_h = '''/* jconfig.h. Generated from jconfig.cfg by configure. */
/* jconfig.cfg --- source file edited by configure script */
/* see jconfig.txt for explanations */
#define HAVE_PROTOTYPES 1
#define HAVE_UNSIGNED_CHAR 1
#define HAVE_UNSIGNED_SHORT 1
/* #undef void */
/* #undef const */
/* #undef CHAR_IS_UNSIGNED */
#define HAVE_STDDEF_H 1
#define HAVE_STDLIB_H 1
#define HAVE_LOCALE_H 1
/* #undef NEED_BSD_STRINGS */
/* #undef NEED_SYS_TYPES_H */
/* #undef NEED_FAR_POINTERS */
/* #undef NEED_SHORT_EXTERNAL_NAMES */
/* Define this if you get warnings about undefined structures. */
/* #undef INCOMPLETE_TYPES_BROKEN */
/* Define "boolean" as unsigned char, not enum, on Windows systems. */
#ifdef _WIN32
#ifndef __RPCNDR_H__ /* don't conflict if rpcndr.h already read */
typedef unsigned char boolean;
#endif
#ifndef FALSE /* in case these macros already exist */
#define FALSE 0 /* values of boolean */
#endif
#ifndef TRUE
#define TRUE 1
#endif
#define HAVE_BOOLEAN /* prevent jmorecfg.h from redefining it */
#endif
#ifdef JPEG_INTERNALS
/* #undef RIGHT_SHIFT_IS_UNSIGNED */
#define INLINE __inline__
/* These are for configuring the JPEG memory manager. */
/* #undef DEFAULT_MAX_MEM */
/* #undef NO_MKTEMP */
#endif /* JPEG_INTERNALS */
#ifdef JPEG_CJPEG_DJPEG
#define BMP_SUPPORTED /* BMP image file format */
#define GIF_SUPPORTED /* GIF image file format */
#define PPM_SUPPORTED /* PBMPLUS PPM/PGM image file format */
/* #undef RLE_SUPPORTED */
#define TARGA_SUPPORTED /* Targa image file format */
/* #undef TWO_FILE_COMMANDLINE */
/* #undef NEED_SIGNAL_CATCHER */
/* #undef DONT_USE_B_MODE */
/* Define this if you want percent-done progress reports from cjpeg/djpeg. */
/* #undef PROGRESS_REPORT */
#endif /* JPEG_CJPEG_DJPEG */
'''

0 comments on commit df9f7f1

Please sign in to comment.