Created
October 26, 2016 06:13
-
-
Save yohhoy/c577f6e08673af4f55d2184f170dbb1d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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