Skip to content

Commit

Permalink
check version 0.4.0
Browse files Browse the repository at this point in the history
  • Loading branch information
le0pard committed Jan 17, 2014
1 parent 901ac30 commit 52cb21d
Show file tree
Hide file tree
Showing 7 changed files with 187 additions and 32 deletions.
2 changes: 1 addition & 1 deletion .ruby-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2.0.0
2.1.0
164 changes: 154 additions & 10 deletions ext/webp_ffi/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,146 @@ static int UtilWritePPM(FILE* fout, const WebPDecBuffer* const buffer, int alpha
return 1;
}

static void PutLE16(uint8_t* const dst, uint32_t value) {
dst[0] = (value >> 0) & 0xff;
dst[1] = (value >> 8) & 0xff;
}

static void PutLE32(uint8_t* const dst, uint32_t value) {
PutLE16(dst + 0, (value >> 0) & 0xffff);
PutLE16(dst + 2, (value >> 16) & 0xffff);
}

#define BMP_HEADER_SIZE 54
static int UtilWriteBMP(FILE* fout, const WebPDecBuffer* const buffer) {
const int has_alpha = (buffer->colorspace != MODE_BGR);
const uint32_t width = buffer->width;
const uint32_t height = buffer->height;
const uint8_t* const rgba = buffer->u.RGBA.rgba;
const int stride = buffer->u.RGBA.stride;
const uint32_t bytes_per_px = has_alpha ? 4 : 3;
uint32_t y;
const uint32_t line_size = bytes_per_px * width;
const uint32_t bmp_stride = (line_size + 3) & ~3; // pad to 4
const uint32_t total_size = bmp_stride * height + BMP_HEADER_SIZE;
uint8_t bmp_header[BMP_HEADER_SIZE] = { 0 };

// bitmap file header
PutLE16(bmp_header + 0, 0x4d42); // signature 'BM'
PutLE32(bmp_header + 2, total_size); // size including header
PutLE32(bmp_header + 6, 0); // reserved
PutLE32(bmp_header + 10, BMP_HEADER_SIZE); // offset to pixel array
// bitmap info header
PutLE32(bmp_header + 14, 40); // DIB header size
PutLE32(bmp_header + 18, width); // dimensions
PutLE32(bmp_header + 22, -(int)height); // vertical flip!
PutLE16(bmp_header + 26, 1); // number of planes
PutLE16(bmp_header + 28, bytes_per_px * 8); // bits per pixel
PutLE32(bmp_header + 30, 0); // no compression (BI_RGB)
PutLE32(bmp_header + 34, 0); // image size (dummy)
PutLE32(bmp_header + 38, 2400); // x pixels/meter
PutLE32(bmp_header + 42, 2400); // y pixels/meter
PutLE32(bmp_header + 46, 0); // number of palette colors
PutLE32(bmp_header + 50, 0); // important color count

// TODO(skal): color profile

// write header
if (fwrite(bmp_header, sizeof(bmp_header), 1, fout) != 1) {
return 0;
}

// write pixel array
for (y = 0; y < height; ++y) {
if (fwrite(rgba + y * stride, line_size, 1, fout) != 1) {
return 0;
}
// write padding zeroes
if (bmp_stride != line_size) {
const uint8_t zeroes[3] = { 0 };
if (fwrite(zeroes, bmp_stride - line_size, 1, fout) != 1) {
return 0;
}
}
}
return 1;
}
#undef BMP_HEADER_SIZE

#define NUM_IFD_ENTRIES 15
#define EXTRA_DATA_SIZE 16
// 10b for signature/header + n * 12b entries + 4b for IFD terminator:
#define EXTRA_DATA_OFFSET (10 + 12 * NUM_IFD_ENTRIES + 4)
#define TIFF_HEADER_SIZE (EXTRA_DATA_OFFSET + EXTRA_DATA_SIZE)

static int UtilWriteTIFF(FILE* fout, const WebPDecBuffer* const buffer) {
const int has_alpha = (buffer->colorspace != MODE_RGB);
const uint32_t width = buffer->width;
const uint32_t height = buffer->height;
const uint8_t* const rgba = buffer->u.RGBA.rgba;
const int stride = buffer->u.RGBA.stride;
const uint8_t bytes_per_px = has_alpha ? 4 : 3;
// For non-alpha case, we omit tag 0x152 (ExtraSamples).
const uint8_t num_ifd_entries = has_alpha ? NUM_IFD_ENTRIES
: NUM_IFD_ENTRIES - 1;
uint8_t tiff_header[TIFF_HEADER_SIZE] = {
0x49, 0x49, 0x2a, 0x00, // little endian signature
8, 0, 0, 0, // offset to the unique IFD that follows
// IFD (offset = 8). Entries must be written in increasing tag order.
num_ifd_entries, 0, // Number of entries in the IFD (12 bytes each).
0x00, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 10: Width (TBD)
0x01, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 22: Height (TBD)
0x02, 0x01, 3, 0, bytes_per_px, 0, 0, 0, // 34: BitsPerSample: 8888
EXTRA_DATA_OFFSET + 0, 0, 0, 0,
0x03, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 46: Compression: none
0x06, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, // 58: Photometric: RGB
0x11, 0x01, 4, 0, 1, 0, 0, 0, // 70: Strips offset:
TIFF_HEADER_SIZE, 0, 0, 0, // data follows header
0x12, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 82: Orientation: topleft
0x15, 0x01, 3, 0, 1, 0, 0, 0, // 94: SamplesPerPixels
bytes_per_px, 0, 0, 0,
0x16, 0x01, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 106: Rows per strip (TBD)
0x17, 0x01, 4, 0, 1, 0, 0, 0, 0, 0, 0, 0, // 118: StripByteCount (TBD)
0x1a, 0x01, 5, 0, 1, 0, 0, 0, // 130: X-resolution
EXTRA_DATA_OFFSET + 8, 0, 0, 0,
0x1b, 0x01, 5, 0, 1, 0, 0, 0, // 142: Y-resolution
EXTRA_DATA_OFFSET + 8, 0, 0, 0,
0x1c, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 154: PlanarConfiguration
0x28, 0x01, 3, 0, 1, 0, 0, 0, 2, 0, 0, 0, // 166: ResolutionUnit (inch)
0x52, 0x01, 3, 0, 1, 0, 0, 0, 1, 0, 0, 0, // 178: ExtraSamples: rgbA
0, 0, 0, 0, // 190: IFD terminator
// EXTRA_DATA_OFFSET:
8, 0, 8, 0, 8, 0, 8, 0, // BitsPerSample
72, 0, 0, 0, 1, 0, 0, 0 // 72 pixels/inch, for X/Y-resolution
};
uint32_t y;

// Fill placeholders in IFD:
PutLE32(tiff_header + 10 + 8, width);
PutLE32(tiff_header + 22 + 8, height);
PutLE32(tiff_header + 106 + 8, height);
PutLE32(tiff_header + 118 + 8, width * bytes_per_px * height);
if (!has_alpha) PutLE32(tiff_header + 178, 0); // IFD terminator

// write header
if (fwrite(tiff_header, sizeof(tiff_header), 1, fout) != 1) {
return 0;
}
// write pixel values
for (y = 0; y < height; ++y) {
if (fwrite(rgba + y * stride, bytes_per_px, width, fout) != width) {
return 0;
}
}

return 1;
}

#undef TIFF_HEADER_SIZE
#undef EXTRA_DATA_OFFSET
#undef EXTRA_DATA_SIZE
#undef NUM_IFD_ENTRIES

static int UtilWriteAlphaPlane(FILE* fout, const WebPDecBuffer* const buffer) {
const uint32_t width = buffer->width;
const uint32_t height = buffer->height;
Expand Down Expand Up @@ -389,11 +529,11 @@ static InputFileFormat GetImageType(FILE* in_file) {

magic = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
if (magic == 0x89504E47U) {
format = PNG_;
format = iPNG_;
} else if (magic >= 0xFFD8FF00U && magic <= 0xFFD8FFFFU) {
format = JPEG_;
format = iJPEG_;
} else if (magic == 0x49492A00 || magic == 0x4D4D002A) {
format = TIFF_;
format = iTIFF_;
}
return format;
}
Expand All @@ -410,11 +550,11 @@ int UtilReadPicture(const char* const filename, WebPPicture* const pic,
if (pic->width == 0 || pic->height == 0) {
// If no size specified, try to decode it as PNG/JPEG (as appropriate).
const InputFileFormat format = GetImageType(in_file);
if (format == PNG_) {
if (format == iPNG_) {
ok = UtilReadPNG(in_file, pic, keep_alpha);
} else if (format == JPEG_) {
} else if (format == iJPEG_) {
ok = UtilReadJPEG(in_file, pic);
} else if (format == TIFF_) {
} else if (format == iTIFF_) {
ok = UtilReadTIFF(filename, pic, keep_alpha);
}
} else {
Expand All @@ -437,13 +577,17 @@ int UtilSaveOutput(const WebPDecBuffer* const buffer,
return 0;
}

if (format == PNG) {
if (format == oPNG) {
ok &= UtilWritePNG(fout, buffer);
} else if (format == PAM) {
} else if (format == oPAM) {
ok &= UtilWritePPM(fout, buffer, 1);
} else if (format == PPM) {
} else if (format == oPPM) {
ok &= UtilWritePPM(fout, buffer, 0);
} else if (format == PGM) {
} else if (format == oBMP) {
ok &= UtilWriteBMP(fout, buffer);
} else if (format == oTIFF_) {
ok &= UtilWriteTIFF(fout, buffer);
} else if (format == oPGM || format == oYUV) {
ok &= UtilWritePGM(fout, buffer);
} else if (format == ALPHA_PLANE_ONLY) {
ok &= UtilWriteAlphaPlane(fout, buffer);
Expand Down
17 changes: 10 additions & 7 deletions ext/webp_ffi/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,20 @@ extern "C" {
#endif

typedef enum {
PNG = 0,
PAM,
PPM,
PGM,
oPNG = 0,
oPAM,
oPPM,
oPGM,
oBMP,
oTIFF_,
oYUV,
ALPHA_PLANE_ONLY // this is for experimenting only
} OutputFileFormat;

typedef enum {
PNG_ = 0,
JPEG_,
TIFF_, // 'TIFF' clashes with libtiff
iPNG_ = 0,
iJPEG_,
iTIFF_, // 'TIFF' clashes with libtiff
UNSUPPORTED
} InputFileFormat;

Expand Down
23 changes: 14 additions & 9 deletions ext/webp_ffi/webp_ffi.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,10 @@
#include <stdlib.h>
#include <string.h>

#include "webp/decode.h"
#include "webp/encode.h"

// utils
#include "./util.h"
#include "./webp_ffi.h"


#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
Expand Down Expand Up @@ -193,7 +189,7 @@ int webp_decode(const char *in_file, const char *out_file, const FfiWebpDecodeCo
WebPDecoderConfig config;
WebPDecBuffer* const output_buffer = &config.output;
WebPBitstreamFeatures* const bitstream = &config.input;
OutputFileFormat format = PNG;
OutputFileFormat format = oPNG;

if (!WebPInitDecoderConfig(&config)) {
//fprintf(stderr, "Library version mismatch!\n");
Expand Down Expand Up @@ -239,16 +235,24 @@ int webp_decode(const char *in_file, const char *out_file, const FfiWebpDecodeCo
}

switch (format) {
case PNG:
case oPNG:
output_buffer->colorspace = bitstream->has_alpha ? MODE_RGBA : MODE_RGB;
break;
case PAM:
case oPAM:
output_buffer->colorspace = MODE_RGBA;
break;
case PPM:
case oPPM:
output_buffer->colorspace = MODE_RGB; // drops alpha for PPM
break;
case PGM:
case oBMP:
output_buffer->colorspace = bitstream->has_alpha ? MODE_BGRA : MODE_BGR;
break;
case oTIFF_: // note: force pre-multiplied alpha
output_buffer->colorspace =
bitstream->has_alpha ? MODE_rgbA : MODE_RGB;
break;
case oPGM:
case oYUV:
output_buffer->colorspace = bitstream->has_alpha ? MODE_YUVA : MODE_YUV;
break;
case ALPHA_PLANE_ONLY:
Expand All @@ -258,6 +262,7 @@ int webp_decode(const char *in_file, const char *out_file, const FfiWebpDecodeCo
free((void*)data);
return 3;
}

status = WebPDecode(data, data_size, &config);

if (status != VP8_STATUS_OK) {
Expand Down
5 changes: 4 additions & 1 deletion lib/webp/c.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ module C
:pam,
:ppm,
:pgm,
:bmp,
:tiff,
:yuv,
:alpha_plane_only )
# struct
class FfiWebpEncodeConfig < FFI::Struct
Expand Down Expand Up @@ -37,7 +40,7 @@ class FfiWebpEncodeConfig < FFI::Struct
:resize_w, :int,
:resize_h, :int
end

class FfiWebpDecodeConfig < FFI::Struct
layout :output_format, OutputFileFormat,
:bypass_filtering, :int,
Expand Down
6 changes: 3 additions & 3 deletions spec/travis_build.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#/usr/bin/env sh
wget http://webp.googlecode.com/files/libwebp-0.3.1.tar.gz
tar xvzf libwebp-0.3.1.tar.gz
cd libwebp-0.3.1
wget http://webp.googlecode.com/files/libwebp-0.4.0.tar.gz
tar xvzf libwebp-0.4.0.tar.gz
cd libwebp-0.4.0
./configure
make
sudo make install
Expand Down
2 changes: 1 addition & 1 deletion spec/webp_ffi_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@
end
end
context "with output_format" do
[:png, :pam, :ppm, :pgm, :alpha_plane_only].each do |output_format|
[:png, :pam, :ppm, :pgm, :bmp, :tiff, :yuv, :alpha_plane_only].each do |output_format|
factories[:webp].take(2).each do |image|
it "#{image}.webp image to #{output_format}" do
in_filename = File.expand_path(File.join(File.dirname(__FILE__), "factories/#{image}.webp"))
Expand Down

0 comments on commit 52cb21d

Please sign in to comment.