直接操作 SDL_Overlay 上的像素

我想在ffplay 视频视频加一些东西比如透明菜单,交互界面等。起初直接把Surface Update,发现每隔大半秒就会闪烁。即使调整double buffer,
Delay 参数也无济于事。通过网上查找,其原因是 Surface 和 Overlay 是两套不同的机制,当Overlay 显示时,就会把相同区域的Surface 覆盖掉,不是真正的“Overlay”(文档里明确的这样说了)。看来Overlay 是绕不过去的了。下面摘录一段原文:
It looks like mplayer has an OSD as well, and it supports almost all RGB and 
YUV color schemes. It looks like they create an alpha buffer based on the 
destination and the image to overlay and use it to make a transparent blit. 

It would be really sweet if this could be integrated into SDL or at least a 
nice little addon to handle YUV_Surface and RGB_Surface. 

那我的一个想法是:
drawline on YUV color space.

经过昨天的摸索和今天一天的编码测试,我主要解决了:
o 在YUV Color Space 上画点和画线。
o 像素格式 RGB 与 YUV420P 之间相互转换。
o 把一个SDL_Surface blit 到 SDL_Overlay 上。

效果图:
直接操作 SDL_Overlay 上的像素_第1张图片
 
下面给出代码:
/*file:sdl_yuv.c
 direct access yuv color space
author: ludi

quick and dirty, but it  is correct:
void drawHLine_(SDL_Overlay *yuv, int sx, int sy, int len)
{
    int w = yuv->pitches[0];
    memset(yuv->pixels[0]+sy*w + sx, 255, len*1);
    memset(yuv->pixels[1]+(sy*w + 2*sx)/4, 255, (len+1)/2);
    memset(yuv->pixels[2]+(sy*w + 2*sx)/4, 255, (len+1)/2);
}

todo: add scalability for blitSurface2YUV (really need this??).
history: 2011.11.05 initial startup.
*/
#include<string.h>
#include<SDL.h>

extern void  rgb2yuv(int r, int g, int b, int *y, int *u, int *v);
extern void  yuv2rgb(int y, int u, int v, int *r, int *g, int *b);

void fillRect(SDL_Overlay *yuv, SDL_Rect *rect, int y0, int u, int v)
{
    int y;
    int size = rect->w;
    int uv_size = (size-1)/2+1;
    int uv_off = 0;

    for(y = 0; y< rect->h; ++y)
    {
    memset(yuv->pixels[0] + (rect->y + y) * yuv->pitches[0] + rect->x, 
    y0, size);
if(y%2 == 0)
{
    memset(yuv->pixels[1] + (uv_off + rect->y /2) * yuv->pitches[1] + rect->x/2, 
    v, uv_size);
    memset(yuv->pixels[2] + (uv_off + rect->y /2) * yuv->pitches[2] + rect->x/2, 
    u, uv_size);
    ++uv_off;
}
    }
}

int blitSurface2YUV(SDL_Surface *src, SDL_Overlay *dst, SDL_Rect *dstrect)
{
    Uint8 r, g, b;
    int y1,u1,v1;
    int y,x;
    int height = src->h < dstrect->h ? src->h: dstrect->h;
    int width =  src->w < dstrect->w ? src->w: dstrect->w;
    int uv_off = 0;
    Uint32 pixel;

    if(dst->format != SDL_YV12_OVERLAY)return 1;

    for(y = 0; y < height; ++y)
    {
for(x = 0; x < width; ++x)
{
    switch(src->format->BitsPerPixel)
    {
case 8:
    pixel = *((Uint8*)src->pixels + y*src->pitch + x);
    break;
case 16:
    pixel = *((Uint16*)src->pixels + y*src->pitch/2 + x);
    break;
case 32:
    pixel = *((Uint32*)src->pixels + y*src->pitch/4 + x);
    break;
default:
    return -1;
    }
    SDL_GetRGB(pixel, src->format, &r, &g, &b);
    rgb2yuv(r, g, b, &y1, &u1, &v1);

memset(dst->pixels[0] + (dstrect->y + y) * dst->pitches[0] + (dstrect->x + x), 
(Uint8)y1, 1);

    if((x%2 == 0 ) && (y%2 == 0 ))
    {
memset(dst->pixels[1] + (uv_off + dstrect->y /2) * dst->pitches[1] + (dstrect->x/2 + x/2), 
(Uint8)v1, 1);
memset(dst->pixels[2] + (uv_off + dstrect->y /2) * dst->pitches[2] + (dstrect->x/2 + x/2), 
(Uint8)u1, 1);
    }
}
if(y%2 == 0)++uv_off;
    }
    return 0;
}


void drawHLine(SDL_Overlay *yuv, int sx, int sy, int len)
{
    SDL_Rect rect = {sx,sy, len, 1};
    fillRect(yuv, &rect, 255,255,255);
}


void drawDot(SDL_Overlay *yuv, int sx, int sy)
{
    drawHLine(yuv, sx,sy, 1);
}

int main(int argc, char **argv)
{
    int w,h;
    SDL_Surface *screen;
    SDL_Overlay *yuv;


    SDL_Init (SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER);
    screen = SDL_SetVideoMode(450,350,0,0);
    yuv = SDL_CreateYUVOverlay(w = 400,h = 300,SDL_YV12_OVERLAY, screen);


    SDL_Rect rect0 = {0,0, w,h};
    fillRect(yuv, &rect0, 50,128,128);//RGB(50,50,50) white color
    
    SDL_Rect rect = {w/2,20,w/5,20};
    fillRect(yuv, &rect, 82,90,240);//RGB(255,0,0);
    rect.y += 30;
    fillRect(yuv, &rect, 144,54,34);//RGB(0,255,0)
    rect.y += 30;
    fillRect(yuv, &rect, 41,240,110);//RGB(0,0,255);
    rect.y +=30;
    fillRect(yuv, &rect, 210,16,146);//RGB(255,255,0);
    rect.y +=30;
    fillRect(yuv, &rect, 107,202,222);//RGB(255,0,255);
    rect.y +=30;
    fillRect(yuv, &rect, 169,166,16); //RGB(0,255,255);
    rect.y +=30;
    fillRect(yuv, &rect, 235,128,128);//RGB(255,255,255);
    rect.y +=30;
    fillRect(yuv, &rect, 16,128,128); //RGB(0,0,0);

    {
SDL_Rect rect2 = {0, 0, w, h/4};//a red banner at top
SDL_FillRect(screen, &rect2, 0xffff0000);
rect2.y = h/4; rect2.w /= 2;//move a little down, show the left half.
blitSurface2YUV(screen, yuv, &rect2);
    }
    
#if 1 //FIXME: drawHLine uses a constant color. 
    drawHLine(yuv, 0,10,w/2);
    
    drawHLine(yuv, 0,20,1);
    drawHLine(yuv, w/2,20,1);
    drawHLine(yuv, w/2,30,4);

    int i;
    for(i=0; i<10; i++)
    {
drawDot(yuv, i*w/10, 40);
    }
#endif
    

    for(;;)
    {//event pump, such that we can move the window with proper saving and restoring bg. 
SDL_Event event;
if(SDL_PollEvent(&event))
{
    if(event.type == SDL_QUIT)break;
    
}
SDL_Rect tmp = {10,10, w, h};
SDL_DisplayYUVOverlay(yuv, &tmp);
SDL_Flip(screen);
SDL_Delay(100);
    }
    
    SDL_FreeSurface(screen);//clean up
    SDL_Quit();
    return 0;
}

/*****************************************************************************************************/
/*file:rgb2yuv.c

NOTE: Y  values are conventionally shifted and scaled to the range [16, 235] rather than using the full range of [0, 255]. 
This confusing practice derives from the MPEG standards and 
explains why 16 is added to Y' and why the Y' coefficients in the basic transform sum to 220 instead of 255. 
U and V values, which may be positive or negative, 
are summed with 128 to make them always positive.[4]

ref http://en.wikipedia.org/wiki/YUV
*/
#include<stdio.h>

void  rgb2yuv(int r, int g, int b, int *y, int *u, int *v)
{
    int y0, u0, v0;

    y0 =  66*r + 129*g +  25*b;
    u0 = -38*r + -74*g + 112*b;
    v0 = 112*r + -94*g + -18*b;

    y0 = (y0+128)>>8;
    u0 = (u0+128)>>8;
    v0 = (v0+128)>>8;

    *y = y0 + 16;
    *u = u0 + 128;
    *v = v0 + 128;
}

void  yuv2rgb(int y, int u, int v, int *r, int *g, int *b)
{
    int r0,g0,b0;
    v = v - 128;
    u = u - 128;
    r0 = y + v + (v>>2) + (v>>3) + (v>>5);
    g0 = y - ((u>>2) + (u>>4) + (u>>5)) - ((v>>1) + (v>>3) + (v>>4) + (v>>5));
    b0 = y + u + (u>>1) + (u>>2) + (u>>6);

    *r = r0 > 255 ? 255: r0;
    *g = g0 > 255 ? 255: g0;
    *b = b0 > 255 ? 255: b0;
}

#if defined(GEN_EXE)

int main(int argc, char **argv)
{
    if(argc < 4)return printf("usage: %s R G B \nor    %s r Y U V\n",argv[0],argv[0]);
    if(argv[1][0] == 'r')
    {
int r,g,b;
yuv2rgb(atoi(argv[2]), atoi(argv[3]), atoi(argv[4]), &r,&g,&b);
printf("%d,%d,%d\n",r,g,b);
    }
    else
    {
int y, u, v;
rgb2yuv(atoi(argv[1]), atoi(argv[2]), atoi(argv[3]), &y, &u, &v);
printf("%d,%d,%d\n", y,u,v);
    }
    return 0;
}
#endif

你可能感兴趣的:(直接操作 SDL_Overlay 上的像素)