I am new to shaders and I need to convert efficiently an RGB image to YUV420.
I can convert the RGB image to YUV420 with a simple c++ program that uses the
long length = m_width * m_height;
long yuv420FrameLength = (float)length * 1.5;
unsigned char *yuvdest = (unsigned char *)malloc(yuv420FrameLength * sizeof(unsigned char));
int r, g, b, y, u, v, ypos, upos, vpos;
for (int j = 0; j < m_height; ++j) {
for (int i = 0; i < m_width; ++i) {
r = (int)m_pRGBImage[(j * m_width + i) * 3 + 0];
g = (int)m_pRGBImage[(j * m_width + i) * 3 + 1];
b = (int)m_pRGBImage[(j * m_width + i) * 3 + 2];
y = ((66 * r + 129 * g + 25 * b + 128) >> 8) + 16;
u = ((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128;
v = ((112 * r - 94 * g - 18 * b + 128) >> 8) + 128;
ypos = j * m_width + i;
upos = (j / 2) * (m_width / 2) + (i / 2) + length;
vpos = (j / 2) * (m_width / 2) + (i / 2) + length + (length / 4);
yuvdest[ypos] = y;
yuvdest[upos] = u;
yuvdest[vpos] = v;
}
}
std::ofstream myfile;
myfile.open(pPath, std::ios_base::trunc);
myfile << yuvdest;
myfile.close();
I can transform the pixels and create the YUV with the following shader code (I did not test it, if I'm wrong, please tell me).
sampler2D sampler0 : register(s0);
struct VS_INPUT
{
float4 Position : POSITION;
float2 Tex0 : TEXCOORD0;
};
struct VS_OUTPUT
{
float4 Position : POSITION;
float2 Tex0 : TEXCOORD0;
};
struct PS_OUTPUT
{
float4 Color : COLOR0;
};
PS_OUTPUT psmain(in VS_OUTPUT In)
{
PS_OUTPUT Out;
Out.Color = tex2D(sampler0, In.Tex0);
int r = Out.Color.x;
int g = Out.Color.y;
int b = Out.Color.z;
float y = ((66 * r + 129 * g + 25 * b + 128) >> 8) + 16;
float u = ((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128;
float v = ((112 * r - 94 * g - 18 * b + 128) >> 8) + 128;;
return Out;
};
VS_OUTPUT vsmain(in VS_INPUT In)
{
VS_OUTPUT Out;
Out.Position = In.Position;
Out.Tex0 = In.Tex0;
return Out;
};
The thing that is missing to me is how to arrange the bytes formation correctly as the C++ code:
ypos = j * m_width + i;
upos = (j / 2) * (m_width / 2) + (i / 2) + length;
vpos = (j / 2) * (m_width / 2) + (i / 2) + length + (length / 4);