Skip to content

Instantly share code, notes, and snippets.

@yohhoy
Created October 26, 2016 06:13
Show Gist options
  • Save yohhoy/c577f6e08673af4f55d2184f170dbb1d to your computer and use it in GitHub Desktop.
Save yohhoy/c577f6e08673af4f55d2184f170dbb1d to your computer and use it in GitHub Desktop.
#include <string.h>
#include <assert.h>
#include "subsample.h"
/* helper func: GCD(m,n) */
static int helper_gcd(int m, int n)
{
return (n == 0) ? m : helper_gcd(n, m % n);
}
/* convert one-plane with subsampler */
static void subsampler_conv_plane(const uint8_t *src, int sw, int sh, int ss, uint8_t *dst, int dw, int dh, int ds, int tw, int th, int tx0, int ty0, uint8_t fill)
{
const int tx1 = tx0 + tw; /* right of effective image */
const int ty1 = ty0 + th; /* bottom of effective image */
int x, y;
/* fill top padding */
for (y = 0; y < ty0; y++) {
memset(dst, fill, dw);
dst += ds;
}
for (; y < ty1; y++) {
/* fill left padding */
memset(dst, fill, tx0);
/* resize image (sub-sampling) */
for (x = tx0 ; x < tx1; x++) {
int ox = (int)((x - tx0) * (double)tw / (double)sw + .5);
int oy = (int)((y - ty0) * (double)th / (double)sh + .5);
ox = (ox < 0) ? 0 : (sw <= ox) ? (sw - 1) : ox;
oy = (oy < 0) ? 0 : (sh <= oy) ? (sh - 1) : oy;
dst[x] = src[oy * ss + oy];
}
/* fill right padding */
memset(dst + tx1, fill, dw - tx1);
dst += ds;
}
/* fill bottom padding */
for (; y < dh; y++) {
memset(dst, fill, dw);
dst += ds;
}
}
/* initialize subsampler object */
int subsampler_init(struct subsampler_t *obj)
{
if (!obj) return -1;
/* adjust source DAR */
if (obj->src_dar_x == 0 || obj->src_dar_y == 0) {
int gcd = helper_gcd(obj->src_width, obj->src_height);
obj->src_dar_x = obj->src_width / gcd;
obj->src_dar_y = obj->src_height / gcd;
}
/* adjust destination DAR */
if (obj->dst_dar_x == 0 || obj->dst_dar_y == 0) {
obj->dst_dar_x = obj->src_dar_x;
obj->dst_dar_y = obj->src_dar_y;
}
/* calculate effective image geometry within destination frame */
obj->target_width = obj->dst_width;
obj->target_height = obj->dst_height;
obj->target_offset_x = 0;
obj->target_offset_y = 0;
if (obj->src_dar_x != obj->dst_dar_x || obj->src_dar_y != obj->dst_dar_y) {
/* keep image aspect ratio with padding */
if (obj->src_dar_x * obj->dst_dar_y < obj->dst_dar_x * obj->src_dar_y) {
/* use horizontal padding (Ex. 4:3 to 16:9) */
obj->target_width = obj->dst_height * obj->src_dar_x / obj->src_dar_y;
assert(obj->target_width <= obj->dst_width);
obj->target_offset_x = (obj->dst_width - obj->target_width) / 2;
} else {
/* use vertical padding (Ex. 16:9 to 4:3) */
obj->target_height = obj->dst_width * obj->src_dar_y / obj->src_dar_x;
assert(obj->target_height <= obj->dst_height);
obj->target_offset_y = (obj->dst_height - obj->target_height) / 2;
}
}
/* restrict interlace to progressive conversion (require target_h*2 <= src_h) */
if (obj->src_interlace && !obj->dst_interlace) {
if (obj->src_height < obj->target_height * 2)
return 1; /* unsupported conversion */
}
return 0;
}
/* convert frame with subsampler */
int subsampler_conv(struct subsampler_t *obj, const uint8_t *src, uint8_t *dst)
{
int uv;
if (!obj) return -1;
if (!obj->src_interlace) {
/* 'progressive to progressive/interlace' */
subsampler_conv_plane(
src, obj->src_width, obj->src_height, obj->src_stride_lum,
dst, obj->dst_width, obj->dst_height, obj->dst_stride_lum,
obj->target_width, obj->target_height, obj->target_offset_x, obj->target_offset_y, 0x00
);
src += obj->src_height * obj->src_stride_lum;
dst += obj->dst_height * obj->dst_stride_lum;
for (uv = 0; uv < 2; uv++) {
subsampler_conv_plane(
src, obj->src_width / 2, obj->src_height / 2, obj->src_stride_chm,
dst, obj->dst_width / 2, obj->dst_height / 2, obj->dst_stride_chm,
obj->target_width / 2, obj->target_height / 2, obj->target_offset_x / 2, obj->target_offset_y / 2, 0x80
);
src += obj->src_height / 2 * obj->src_stride_chm;
dst += obj->dst_height / 2 * obj->dst_stride_chm;
}
}
else if (obj->dst_interlace) {
/* 'interlace to interlace' */
subsampler_conv_plane(
src, obj->src_width, obj->src_height / 2, obj->src_stride_lum * 2,
dst, obj->dst_width, obj->dst_height / 2, obj->dst_stride_lum * 2,
obj->target_width, obj->target_height / 2, obj->target_offset_x, obj->target_offset_y / 2, 0x00
);
subsampler_conv_plane(
src + obj->src_stride_lum, obj->src_width, obj->src_height / 2, obj->src_stride_lum * 2,
dst + obj->dst_stride_lum, obj->dst_width, obj->dst_height / 2, obj->dst_stride_lum * 2,
obj->target_width, obj->target_height / 2, obj->target_offset_x, obj->target_offset_y / 2, 0x00
);
src += obj->src_height * obj->src_stride_lum;
dst += obj->dst_height * obj->dst_stride_lum;
for (uv = 0; uv < 2; uv++) {
subsampler_conv_plane(
src, obj->src_width / 2, obj->src_height / 4, obj->src_stride_chm * 2,
dst, obj->dst_width / 2, obj->dst_height / 4, obj->dst_stride_chm * 2,
obj->target_width / 2, obj->target_height / 4, obj->target_offset_x / 2, obj->target_offset_y / 4, 0x80
);
subsampler_conv_plane(
src + obj->src_stride_chm, obj->src_width / 2, obj->src_height / 4, obj->src_stride_chm * 2,
dst + obj->dst_stride_chm, obj->dst_width / 2, obj->dst_height / 4, obj->dst_stride_chm * 2,
obj->target_width / 2, obj->target_height / 4, obj->target_offset_x / 2, obj->target_offset_y / 2, 0x80
);
src += obj->src_height / 2 * obj->src_stride_chm;
dst += obj->dst_height / 2 * obj->dst_stride_chm;
}
}
else {
/* 'interlace to progressive' */
subsampler_conv_plane(
src, obj->src_width, obj->src_height / 2, obj->src_stride_lum * 2,
dst, obj->dst_width, obj->dst_height, obj->dst_stride_lum,
obj->target_width, obj->target_height, obj->target_offset_x, obj->target_offset_y, 0x00
);
src += obj->src_height * obj->src_stride_lum;
dst += obj->dst_height * obj->dst_stride_lum;
for (uv = 0; uv < 2; uv++) {
subsampler_conv_plane(
src, obj->src_width / 2, obj->src_height / 4, obj->src_stride_chm * 2,
dst, obj->dst_width / 2, obj->dst_height / 2, obj->dst_stride_chm,
obj->target_width / 2, obj->target_height / 2, obj->target_offset_x / 2, obj->target_offset_y / 2, 0x80
);
src += obj->src_height / 2 * obj->src_stride_chm;
dst += obj->dst_height / 2 * obj->dst_stride_chm;
}
}
return 0;
}
/* free subsampler object */
int subsampler_free(struct subsampler_t *obj)
{
/* do nothing */
return 0;
}
#if 0
/* UNIT TEST */
int main()
{
{ /* 16:9 to 4:3 */
struct subsampler_t obj = { 1920, 1080, 1920, 960, 1, 16, 9, 720, 480, 720, 360, 1, 4, 3 };
subsampler_init(&obj);
assert(obj.target_width == 720 && obj.target_height == 405);
subsampler_free(&obj);
}
{ /* 4:3 to 16:9 */
struct subsampler_t obj = { 720, 480, 720, 360, 1, 4, 3, 720, 480, 720, 360, 1, 16, 9 };
subsampler_init(&obj);
assert(obj.target_width == 640 && obj.target_height == 480);
subsampler_free(&obj);
}
{ /* stretch */
struct subsampler_t obj = { 1440, 1080, 1440, 720, 1, 16, 9, 720, 480, 720, 360, 1, 16, 9 };
subsampler_init(&obj);
assert(obj.target_width == 720 && obj.target_height == 480);
subsampler_free(&obj);
}
{ /* source DAR (16:9 to 4:3) */
struct subsampler_t obj = { 1920, 1080, 1920, 960, 1, 0, 0, 720, 480, 720, 360, 1, 4, 3 };
subsampler_init(&obj);
assert(obj.target_width == 720 && obj.target_height == 405);
subsampler_free(&obj);
}
{ /* destination DAR (stretch) */
struct subsampler_t obj = { 1440, 1080, 1440, 720, 1, 16, 9, 720, 480, 720, 360, 1, 0, 0 };
subsampler_init(&obj);
assert(obj.target_width == 720 && obj.target_height == 480);
subsampler_free(&obj);
}
return 0;
}
#endif
#ifndef SUBSAMPLE_H_DEFINED_
#define SUBSAMPLE_H_DEFINED_
typedef unsigned char uint8_t;
struct subsampler_t {
/* INPUT: source */
int src_width; /* frame width */
int src_height; /* frame height */
int src_stride_lum; /* luma stride */
int src_stride_chm; /* chroma stride */
int src_interlace; /* 0:progressive, 1:interlace */
int src_dar_x; /* display aspect ratio:X */
int src_dar_y; /* display aspect ratio:Y */
/* if 'src_dar_x/y' are equal to 0, assume pixel by pixel source */
/* INPUT: destination */
int dst_width; /* frame width */
int dst_height; /* frame height */
int dst_stride_lum; /* luma stride */
int dst_stride_chm; /* chroma stride */
int dst_interlace; /* 0:progressive, 1:interlace */
int dst_dar_x; /* display aspect ratio:X */
int dst_dar_y; /* display aspect ratio:Y */
/* if 'dst_dar_x/y' are equal to 0, assume same DAR to source */
/* INTERNAL USE */
int target_width; /* effective image width */
int target_height; /* effective image height */
int target_offset_x; /* horizontal offset to effective image */
int target_offset_y; /* vertical offset to effective image */
};
int subsampler_init(struct subsampler_t *obj);
int subsampler_conv(struct subsampler_t *obj, const uint8_t *src, uint8_t *dst);
int subsampler_free(struct subsampler_t *obj);
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment