view src/video/photon/SDL_phyuv.c @ 2993:2fad80c77c17

Technically more correct
author Sam Lantinga <slouken@libsdl.org>
date Sun, 04 Jan 2009 19:26:50 +0000
parents 99210400e8b9
children 8f4ed5ec2b06
line wrap: on
line source

/*
    SDL - Simple DirectMedia Layer
    Copyright (C) 1997-2009 Sam Lantinga

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

    Sam Lantinga
    slouken@libsdl.org
*/
#include "SDL_config.h"

/* This is the QNX Realtime Platform version of SDL YUV video overlays */

#include <errno.h>

#include <Ph.h>
#include <Pt.h>

#include "SDL_video.h"
#include "SDL_phyuv_c.h"
#include "../SDL_yuvfuncs.h"

#define OVERLAY_STATE_UNINIT 0
#define OVERLAY_STATE_ACTIVE 1

/* The functions are used to manipulate software video overlays */
static struct private_yuvhwfuncs ph_yuvfuncs = {
    ph_LockYUVOverlay,
    ph_UnlockYUVOverlay,
    ph_DisplayYUVOverlay,
    ph_FreeYUVOverlay
};

int
grab_ptrs2(PgVideoChannel_t * channel, FRAMEDATA * Frame0, FRAMEDATA * Frame1)
{
    int planes = 0;

    /* Buffers have moved; re-obtain the pointers */
    Frame0->Y = (unsigned char *) PdGetOffscreenContextPtr(channel->yplane1);
    Frame1->Y = (unsigned char *) PdGetOffscreenContextPtr(channel->yplane2);
    Frame0->U = (unsigned char *) PdGetOffscreenContextPtr(channel->vplane1);
    Frame1->U = (unsigned char *) PdGetOffscreenContextPtr(channel->vplane2);
    Frame0->V = (unsigned char *) PdGetOffscreenContextPtr(channel->uplane1);
    Frame1->V = (unsigned char *) PdGetOffscreenContextPtr(channel->uplane2);

    if (Frame0->Y)
        planes++;

    if (Frame0->U)
        planes++;

    if (Frame0->V)
        planes++;

    return planes;
}

SDL_Overlay *
ph_CreateYUVOverlay(_THIS, int width, int height, Uint32 format,
                    SDL_Surface * display)
{
    SDL_Overlay *overlay;
    struct private_yuvhwdata *hwdata;
    int vidport;
    int rtncode;
    int planes;
    int i = 0;
    PhPoint_t pos;

    /* Create the overlay structure */
    overlay = SDL_calloc(1, sizeof(SDL_Overlay));

    if (overlay == NULL) {
        SDL_OutOfMemory();
        return NULL;
    }

    /* Fill in the basic members */
    overlay->format = format;
    overlay->w = width;
    overlay->h = height;
    overlay->hwdata = NULL;

    /* Set up the YUV surface function structure */
    overlay->hwfuncs = &ph_yuvfuncs;

    /* Create the pixel data and lookup tables */
    hwdata = SDL_calloc(1, sizeof(struct private_yuvhwdata));

    if (hwdata == NULL) {
        SDL_OutOfMemory();
        SDL_FreeYUVOverlay(overlay);
        return NULL;
    }

    overlay->hwdata = hwdata;

    PhDCSetCurrent(0);
    if (overlay->hwdata->channel == NULL) {
        if ((overlay->hwdata->channel =
             PgCreateVideoChannel(Pg_VIDEO_CHANNEL_SCALER, 0)) == NULL) {
            SDL_SetError
                ("ph_CreateYUVOverlay(): Create channel failed: %s\n",
                 strerror(errno));
            SDL_FreeYUVOverlay(overlay);
            return NULL;

        }
    }

    overlay->hwdata->forcedredraw = 0;

    PtGetAbsPosition(window, &pos.x, &pos.y);
    overlay->hwdata->CurrentWindowPos.x = pos.x;
    overlay->hwdata->CurrentWindowPos.y = pos.y;
    overlay->hwdata->CurrentViewPort.pos.x = 0;
    overlay->hwdata->CurrentViewPort.pos.y = 0;
    overlay->hwdata->CurrentViewPort.size.w = width;
    overlay->hwdata->CurrentViewPort.size.h = height;
    overlay->hwdata->State = OVERLAY_STATE_UNINIT;
    overlay->hwdata->FrameData0 =
        (FRAMEDATA *) SDL_calloc(1, sizeof(FRAMEDATA));
    overlay->hwdata->FrameData1 =
        (FRAMEDATA *) SDL_calloc(1, sizeof(FRAMEDATA));

    vidport = -1;
    i = 0;

    overlay->hwdata->ischromakey = 0;

    do {
        SDL_memset(&overlay->hwdata->caps, 0x00, sizeof(PgScalerCaps_t));
        overlay->hwdata->caps.size = sizeof(PgScalerCaps_t);
        rtncode =
            PgGetScalerCapabilities(overlay->hwdata->channel, i,
                                    &overlay->hwdata->caps);
        if (rtncode == 0) {
            if (overlay->hwdata->caps.format == format) {
                if ((overlay->hwdata->
                     caps.flags & Pg_SCALER_CAP_DST_CHROMA_KEY) ==
                    Pg_SCALER_CAP_DST_CHROMA_KEY) {
                    overlay->hwdata->ischromakey = 1;
                }
                vidport = 1;
                break;
            }
        } else {
            break;
        }
        i++;
    } while (1);


    if (vidport == -1) {
        SDL_SetError("No available video ports for requested format\n");
        SDL_FreeYUVOverlay(overlay);
        return NULL;
    }

    overlay->hwdata->format = format;
    overlay->hwdata->props.format = format;
    overlay->hwdata->props.size = sizeof(PgScalerProps_t);
    overlay->hwdata->props.src_dim.w = width;
    overlay->hwdata->props.src_dim.h = height;

    /* overlay->hwdata->chromakey = PgGetOverlayChromaColor(); */
    overlay->hwdata->chromakey = PgRGB(12, 6, 12);      /* very dark pink color */
    overlay->hwdata->props.color_key = overlay->hwdata->chromakey;

    PhAreaToRect(&overlay->hwdata->CurrentViewPort,
                 &overlay->hwdata->props.viewport);

    overlay->hwdata->props.flags = Pg_SCALER_PROP_DOUBLE_BUFFER;

    if ((overlay->hwdata->ischromakey) && (overlay->hwdata->chromakey)) {
        overlay->hwdata->props.flags |= Pg_SCALER_PROP_CHROMA_ENABLE;
        overlay->hwdata->props.flags |=
            Pg_SCALER_PROP_CHROMA_SPECIFY_KEY_MASK;
    } else {
        overlay->hwdata->props.flags &= ~Pg_SCALER_PROP_CHROMA_ENABLE;
    }

    rtncode =
        PgConfigScalerChannel(overlay->hwdata->channel,
                              &overlay->hwdata->props);

    switch (rtncode) {
    case -1:
        SDL_SetError("PgConfigScalerChannel failed\n");
        SDL_FreeYUVOverlay(overlay);
        return NULL;
    case 1:
    case 0:
    default:
        break;
    }

    planes =
        grab_ptrs2(overlay->hwdata->channel, overlay->hwdata->FrameData0,
                   overlay->hwdata->FrameData1);

    if (overlay->hwdata->channel->yplane1 != NULL)
        overlay->hwdata->YStride = overlay->hwdata->channel->yplane1->pitch;
    if (overlay->hwdata->channel->vplane1 != NULL)
        overlay->hwdata->UStride = overlay->hwdata->channel->vplane1->pitch;
    if (overlay->hwdata->channel->uplane1 != NULL)
        overlay->hwdata->VStride = overlay->hwdata->channel->uplane1->pitch;

    /* check for the validness of all planes */
    if ((overlay->hwdata->channel->yplane1 == NULL) &&
        (overlay->hwdata->channel->uplane1 == NULL) &&
        (overlay->hwdata->channel->vplane1 == NULL)) {
        SDL_FreeYUVOverlay(overlay);
        SDL_SetError("PgConfigScaler() returns all planes equal NULL\n");
        return NULL;
    }
/*
    overlay->hwdata->current = PgNextVideoFrame(overlay->hwdata->channel);

    if (overlay->hwdata->current==0)
    {
        overlay->hwdata->CurrentFrameData = overlay->hwdata->FrameData0;
    }
    else
    {
        overlay->hwdata->CurrentFrameData = overlay->hwdata->FrameData1;
    }
*/
    overlay->hwdata->CurrentFrameData = overlay->hwdata->FrameData0;

/*
    overlay->hwdata->locked = 1;
*/

    /* Find the pitch and offset values for the overlay */
    overlay->planes = planes;
    overlay->pitches = SDL_calloc(overlay->planes, sizeof(Uint16));
    overlay->pixels = SDL_calloc(overlay->planes, sizeof(Uint8 *));
    if (!overlay->pitches || !overlay->pixels) {
        SDL_OutOfMemory();
        SDL_FreeYUVOverlay(overlay);
        return (NULL);
    }

    if (overlay->planes > 0) {
        overlay->pitches[0] = overlay->hwdata->channel->yplane1->pitch;
        overlay->pixels[0] = overlay->hwdata->CurrentFrameData->Y;
    }
    if (overlay->planes > 1) {
        overlay->pitches[1] = overlay->hwdata->channel->vplane1->pitch;
        overlay->pixels[1] = overlay->hwdata->CurrentFrameData->U;
    }
    if (overlay->planes > 2) {
        overlay->pitches[2] = overlay->hwdata->channel->uplane1->pitch;
        overlay->pixels[2] = overlay->hwdata->CurrentFrameData->V;
    }

    overlay->hwdata->State = OVERLAY_STATE_ACTIVE;
    overlay->hwdata->scaler_on = 0;
    overlay->hw_overlay = 1;

    current_overlay = overlay;

    return overlay;
}

int
ph_LockYUVOverlay(_THIS, SDL_Overlay * overlay)
{
    if (overlay == NULL) {
        return -1;
    }

    overlay->hwdata->locked = 1;

/*  overlay->hwdata->current = PgNextVideoFrame(overlay->hwdata->channel);
    if (overlay->hwdata->current == -1)
    {
        SDL_SetError("ph_LockYUVOverlay: PgNextFrame() failed, bailing out\n");
        SDL_FreeYUVOverlay(overlay);
        return 0;
    }

    if (overlay->hwdata->current == 0)
    {
        overlay->hwdata->CurrentFrameData = overlay->hwdata->FrameData0;
    }
    else
    {
        overlay->hwdata->CurrentFrameData = overlay->hwdata->FrameData1;
    }

    if (overlay->planes > 0)
    {
        overlay->pitches[0] = overlay->hwdata->channel->yplane1->pitch;
        overlay->pixels[0]  = overlay->hwdata->CurrentFrameData->Y;
    }
    if (overlay->planes > 1)
    {
        overlay->pitches[1] = overlay->hwdata->channel->uplane1->pitch;
        overlay->pixels[1]  = overlay->hwdata->CurrentFrameData->U;
    }
    if (overlay->planes > 2)
    {
        overlay->pitches[2] = overlay->hwdata->channel->vplane1->pitch;
        overlay->pixels[2]  = overlay->hwdata->CurrentFrameData->V;
    }
*/

    return (0);
}

void
ph_UnlockYUVOverlay(_THIS, SDL_Overlay * overlay)
{
    if (overlay == NULL) {
        return;
    }

    overlay->hwdata->locked = 0;
}

int
ph_DisplayYUVOverlay(_THIS, SDL_Overlay * overlay, SDL_Rect * src,
                     SDL_Rect * dst)
{
    int rtncode;
    PhPoint_t pos;
    SDL_Rect backrect;
    PhRect_t windowextent;
    int winchanged = 0;

    if ((overlay == NULL) || (overlay->hwdata == NULL)) {
        return -1;
    }

    if (overlay->hwdata->State == OVERLAY_STATE_UNINIT) {
        return -1;
    }

    PtGetAbsPosition(window, &pos.x, &pos.y);
    if ((pos.x != overlay->hwdata->CurrentWindowPos.x) ||
        (pos.y != overlay->hwdata->CurrentWindowPos.y)) {
        winchanged = 1;
        overlay->hwdata->CurrentWindowPos.x = pos.x;
        overlay->hwdata->CurrentWindowPos.y = pos.y;
    }

    /* If CurrentViewPort position/size has been changed, then move/resize the viewport */
    if ((overlay->hwdata->CurrentViewPort.pos.x != dst->x) ||
        (overlay->hwdata->CurrentViewPort.pos.y != dst->y) ||
        (overlay->hwdata->CurrentViewPort.size.w != dst->w) ||
        (overlay->hwdata->CurrentViewPort.size.h != dst->h) ||
        (overlay->hwdata->scaler_on == 0) || (winchanged == 1) ||
        (overlay->hwdata->forcedredraw == 1)) {

        if (overlay->hwdata->ischromakey == 1) {
            /* restore screen behind the overlay/chroma color. */
            backrect.x = overlay->hwdata->CurrentViewPort.pos.x;
            backrect.y = overlay->hwdata->CurrentViewPort.pos.y;
            backrect.w = overlay->hwdata->CurrentViewPort.size.w;
            backrect.h = overlay->hwdata->CurrentViewPort.size.h;
            this->UpdateRects(this, 1, &backrect);

            /* Draw the new rectangle of the chroma color at the viewport position */
            PgSetFillColor(overlay->hwdata->chromakey);
            PgDrawIRect(dst->x, dst->y, dst->x + dst->w - 1,
                        dst->y + dst->h - 1, Pg_DRAW_FILL);
            PgFlush();
        }

        overlay->hwdata->props.flags |= Pg_SCALER_PROP_SCALER_ENABLE;
        overlay->hwdata->scaler_on = 1;

        PhWindowQueryVisible(Ph_QUERY_CONSOLE, 0, PtWidgetRid(window),
                             &windowextent);
        overlay->hwdata->CurrentViewPort.pos.x =
            pos.x - windowextent.ul.x + dst->x;
        overlay->hwdata->CurrentViewPort.pos.y =
            pos.y - windowextent.ul.y + dst->y;
        overlay->hwdata->CurrentViewPort.size.w = dst->w;
        overlay->hwdata->CurrentViewPort.size.h = dst->h;
        PhAreaToRect(&overlay->hwdata->CurrentViewPort,
                     &overlay->hwdata->props.viewport);
        overlay->hwdata->CurrentViewPort.pos.x = dst->x;
        overlay->hwdata->CurrentViewPort.pos.y = dst->y;

        rtncode =
            PgConfigScalerChannel(overlay->hwdata->channel,
                                  &(overlay->hwdata->props));

        switch (rtncode) {
        case -1:
            SDL_SetError("PgConfigScalerChannel() function failed\n");
            SDL_FreeYUVOverlay(overlay);
            return -1;
        case 1:
            grab_ptrs2(overlay->hwdata->channel,
                       overlay->hwdata->FrameData0,
                       overlay->hwdata->FrameData1);
            break;
        case 0:
        default:
            break;
        }
    }


/*
    if (overlay->hwdata->locked==0)
    {
        overlay->hwdata->current = PgNextVideoFrame(overlay->hwdata->channel);
        if (overlay->hwdata->current == -1)
        {
            SDL_SetError("ph_LockYUVOverlay: PgNextFrame() failed, bailing out\n");
            SDL_FreeYUVOverlay(overlay);
            return 0;
        }

        if (overlay->hwdata->current == 0)
        {
            overlay->hwdata->CurrentFrameData = overlay->hwdata->FrameData0;
        }
        else
        {
            overlay->hwdata->CurrentFrameData = overlay->hwdata->FrameData1;
        }

        if (overlay->planes > 0)
        {
            overlay->pitches[0] = overlay->hwdata->channel->yplane1->pitch;
            overlay->pixels[0]  = overlay->hwdata->CurrentFrameData->Y;
        }
        if (overlay->planes > 1)
        {
            overlay->pitches[1] = overlay->hwdata->channel->uplane1->pitch;
            overlay->pixels[1]  = overlay->hwdata->CurrentFrameData->U;
        }
        if (overlay->planes > 2)
        {
            overlay->pitches[2] = overlay->hwdata->channel->vplane1->pitch;
            overlay->pixels[2]  = overlay->hwdata->CurrentFrameData->V;
        }
    }
*/

    return 0;
}

void
ph_FreeYUVOverlay(_THIS, SDL_Overlay * overlay)
{
    SDL_Rect backrect;

    if (overlay == NULL) {
        return;
    }

    if (overlay->hwdata == NULL) {
        return;
    }

    current_overlay = NULL;

    /* restore screen behind the overlay/chroma color. */
    backrect.x = overlay->hwdata->CurrentViewPort.pos.x;
    backrect.y = overlay->hwdata->CurrentViewPort.pos.y;
    backrect.w = overlay->hwdata->CurrentViewPort.size.w;
    backrect.h = overlay->hwdata->CurrentViewPort.size.h;
    this->UpdateRects(this, 1, &backrect);

    /* it is need for some buggy drivers, that can't hide overlay before */
    /* freeing buffer, so we got trash on the srceen                     */
    overlay->hwdata->props.flags &= ~Pg_SCALER_PROP_SCALER_ENABLE;
    PgConfigScalerChannel(overlay->hwdata->channel,
                          &(overlay->hwdata->props));

    overlay->hwdata->scaler_on = 0;
    overlay->hwdata->State = OVERLAY_STATE_UNINIT;

    if (overlay->hwdata->channel != NULL) {
        PgDestroyVideoChannel(overlay->hwdata->channel);
        overlay->hwdata->channel = NULL;
        return;
    }

    overlay->hwdata->CurrentFrameData = NULL;

    SDL_free(overlay->hwdata->FrameData0);
    SDL_free(overlay->hwdata->FrameData1);
    overlay->hwdata->FrameData0 = NULL;
    overlay->hwdata->FrameData1 = NULL;
    SDL_free(overlay->hwdata);
}

/* vi: set ts=4 sw=4 expandtab: */