<commit_before>#include <vdpau/vdpau.h>
#include <vdpau/vdpau_x11.h>
#include <stdio.h>
#include <assert.h>
#include <glib.h>
#include <libswscale/swscale.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include "reverse-constant.h"
#include "handle-storage.h"

#ifndef NDEBUG
    #define TRACE(str, ...)  printf("[VDPFAKE] " str "\n", __VA_ARGS__)
    #define TRACE1(str)  printf("[VDPFAKE] " str "\n")
#else
    #define TRACE(str, ...)
    #define TRACE1(str)
#endif

char const *implemetation_description_string = "VAAPI/software backend for VDPAU";

static
uint32_t
rgba_format_storage_size(VdpRGBAFormat rgba_format)
{
    switch (rgba_format) {
    case VDP_RGBA_FORMAT_B8G8R8A8:
    case VDP_RGBA_FORMAT_R8G8B8A8:
    case VDP_RGBA_FORMAT_R10G10B10A2:
    case VDP_RGBA_FORMAT_B10G10R10A2:
        return 4;
    case VDP_RGBA_FORMAT_A8:
        return 1;
    default:
        return 4;
    }
}

static
uint32_t
chroma_storage_size_divider(VdpChromaType chroma_type)
{
    switch (chroma_type) {
    case VDP_CHROMA_TYPE_420: return 4;
    case VDP_CHROMA_TYPE_422: return 2;
    case VDP_CHROMA_TYPE_444: return 1;
    default: return 1;
    }
}

static
const char *
fakeVdpGetErrorString(VdpStatus status)
{
    switch (status) {
    case VDP_STATUS_OK:
        return "VDP_STATUS_OK";
        break;
    case VDP_STATUS_NO_IMPLEMENTATION:
        return "VDP_STATUS_NO_IMPLEMENTATION";
        break;
    case VDP_STATUS_DISPLAY_PREEMPTED:
        return "VDP_STATUS_DISPLAY_PREEMPTED";
        break;
    case VDP_STATUS_INVALID_HANDLE:
        return "VDP_STATUS_INVALID_HANDLE";
        break;
    case VDP_STATUS_INVALID_POINTER:
        return "VDP_STATUS_INVALID_POINTER";
        break;
    case VDP_STATUS_INVALID_CHROMA_TYPE:
        return "VDP_STATUS_INVALID_CHROMA_TYPE";
        break;
    case VDP_STATUS_INVALID_Y_CB_CR_FORMAT:
        return "VDP_STATUS_INVALID_Y_CB_CR_FORMAT";
        break;
    case VDP_STATUS_INVALID_RGBA_FORMAT:
        return "VDP_STATUS_INVALID_RGBA_FORMAT";
        break;
    case VDP_STATUS_INVALID_INDEXED_FORMAT:
        return "VDP_STATUS_INVALID_INDEXED_FORMAT";
        break;
    case VDP_STATUS_INVALID_COLOR_STANDARD:
        return "VDP_STATUS_INVALID_COLOR_STANDARD";
        break;
    case VDP_STATUS_INVALID_COLOR_TABLE_FORMAT:
        return "VDP_STATUS_INVALID_COLOR_TABLE_FORMAT";
        break;
    case VDP_STATUS_INVALID_BLEND_FACTOR:
        return "VDP_STATUS_INVALID_BLEND_FACTOR";
        break;
    case VDP_STATUS_INVALID_BLEND_EQUATION:
        return "VDP_STATUS_INVALID_BLEND_EQUATION";
        break;
    case VDP_STATUS_INVALID_FLAG:
        return "VDP_STATUS_INVALID_FLAG";
        break;
    case VDP_STATUS_INVALID_DECODER_PROFILE:
        return "VDP_STATUS_INVALID_DECODER_PROFILE";
        break;
    case VDP_STATUS_INVALID_VIDEO_MIXER_FEATURE:
        return "VDP_STATUS_INVALID_VIDEO_MIXER_FEATURE";
        break;
    case VDP_STATUS_INVALID_VIDEO_MIXER_PARAMETER:
        return "VDP_STATUS_INVALID_VIDEO_MIXER_PARAMETER";
        break;
    case VDP_STATUS_INVALID_VIDEO_MIXER_ATTRIBUTE:
        return "VDP_STATUS_INVALID_VIDEO_MIXER_ATTRIBUTE";
        break;
    case VDP_STATUS_INVALID_VIDEO_MIXER_PICTURE_STRUCTURE:
        return "VDP_STATUS_INVALID_VIDEO_MIXER_PICTURE_STRUCTURE";
        break;
    case VDP_STATUS_INVALID_FUNC_ID:
        return "VDP_STATUS_INVALID_FUNC_ID";
        break;
    case VDP_STATUS_INVALID_SIZE:
        return "VDP_STATUS_INVALID_SIZE";
        break;
    case VDP_STATUS_INVALID_VALUE:
        return "VDP_STATUS_INVALID_VALUE";
        break;
    case VDP_STATUS_INVALID_STRUCT_VERSION:
        return "VDP_STATUS_INVALID_STRUCT_VERSION";
        break;
    case VDP_STATUS_RESOURCES:
        return "VDP_STATUS_RESOURCES";
        break;
    case VDP_STATUS_HANDLE_DEVICE_MISMATCH:
        return "VDP_STATUS_HANDLE_DEVICE_MISMATCH";
        break;
    case VDP_STATUS_ERROR:
        return "VDP_STATUS_ERROR";
        break;
    default:
        return "VDP Unknown error";
        break;
    } // switch
}

static
VdpStatus
fakeVdpGetApiVersion(uint32_t *api_version)
{
    TRACE1("{full} VdpGetApiVersion");
    *api_version = VDPAU_VERSION;
    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpDecoderQueryCapabilities(VdpDevice device, VdpDecoderProfile profile, VdpBool *is_supported,
                                uint32_t *max_level, uint32_t *max_macroblocks,
                                uint32_t *max_width, uint32_t *max_height)
{
    TRACE1("{zilch} VdpDecoderQueryCapabilities");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpDecoderCreate(VdpDevice device, VdpDecoderProfile profile, uint32_t width, uint32_t height,
                     uint32_t max_references, VdpDecoder *decoder)
{
    TRACE1("{zilch} VdpDecoderCreate");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpDecoderDestroy(VdpDecoder decoder)
{
    TRACE1("{zilch} VdpDecoderDestroy");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpDecoderGetParameters(VdpDecoder decoder, VdpDecoderProfile *profile,
                            uint32_t *width, uint32_t *height)
{
    TRACE1("{zilch} VdpDecoderGetParameters");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpDecoderRender(VdpDecoder decoder, VdpVideoSurface target,
                     VdpPictureInfo const *picture_info, uint32_t bitstream_buffer_count,
                     VdpBitstreamBuffer const *bitstream_buffers)
{
    TRACE1("{zilch} VdpDecoderRender");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpOutputSurfaceQueryCapabilities(VdpDevice device, VdpRGBAFormat surface_rgba_format,
                                      VdpBool *is_supported, uint32_t *max_width,
                                      uint32_t *max_height)
{
    TRACE1("{zilch} VdpOutputSurfaceQueryCapabilities");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpOutputSurfaceQueryGetPutBitsNativeCapabilities(VdpDevice device,
                                                      VdpRGBAFormat surface_rgba_format,
                                                      VdpBool *is_supported)
{
    TRACE1("{zilch} VdpOutputSurfaceQueryGetPutBitsNativeCapabilities");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpOutputSurfaceQueryPutBitsIndexedCapabilities(VdpDevice device,
                                                    VdpRGBAFormat surface_rgba_format,
                                                    VdpIndexedFormat bits_indexed_format,
                                                    VdpColorTableFormat color_table_format,
                                                    VdpBool *is_supported)
{
    TRACE1("{zilch} VdpOutputSurfaceQueryPutBitsIndexedCapabilities");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpOutputSurfaceQueryPutBitsYCbCrCapabilities(VdpDevice device,
                                                  VdpRGBAFormat surface_rgba_format,
                                                  VdpYCbCrFormat bits_ycbcr_format,
                                                  VdpBool *is_supported)
{
    TRACE1("{zilch} VdpOutputSurfaceQueryPutBitsYCbCrCapabilities");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpOutputSurfaceCreate(VdpDevice device, VdpRGBAFormat rgba_format, uint32_t width,
                           uint32_t height, VdpOutputSurface *surface)
{
    TRACE("{part} VdpOutputSurfaceCreate device=%d, rgba_format=%s, width=%d, height=%d",
        device, reverse_rgba_format(rgba_format), width, height);
    if (! handlestorage_valid(device, HANDLE_TYPE_DEVICE))
        return VDP_STATUS_INVALID_HANDLE;

    if (width > 4096 || height > 4096)
        return VDP_STATUS_INVALID_SIZE;

    VdpOutputSurfaceData *data = (VdpOutputSurfaceData *)calloc(1, sizeof(VdpOutputSurfaceData));
    if (NULL == data)
        return VDP_STATUS_RESOURCES;

    uint32_t const stride = (width % 4 == 0) ? width : (width & ~0x3UL) + 4;

    data->type = HANDLE_TYPE_OUTPUT_SURFACE;
    data->device = device;
    data->rgba_format = rgba_format;
    data->width = width;
    data->stride = stride;
    data->height = height;
    data->buf = malloc(stride * height * rgba_format_storage_size(rgba_format));

    if (NULL == data->buf) {
        free(data);
        return VDP_STATUS_RESOURCES;
    }

    *surface = handlestorage_add(data);

    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpOutputSurfaceDestroy(VdpOutputSurface surface)
{
    TRACE("{full} VdpOutputSurfaceDestroy surface=%d", surface);
    VdpOutputSurfaceData *data = handlestorage_get(surface, HANDLE_TYPE_OUTPUT_SURFACE);
    if (NULL == data)
        return VDP_STATUS_INVALID_HANDLE;

    free(data->buf);
    free(data);
    handlestorage_expunge(surface);
    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpOutputSurfaceGetParameters(VdpOutputSurface surface, VdpRGBAFormat *rgba_format,
                                  uint32_t *width, uint32_t *height)
{
    TRACE1("{zilch} VdpOutputSurfaceGetParameters");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpOutputSurfaceGetBitsNative(VdpOutputSurface surface, VdpRect const *source_rect,
                                  void *const *destination_data,
                                  uint32_t const *destination_pitches)
{
    TRACE1("{zilch} VdpOutputSurfaceGetBitsNative");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpOutputSurfacePutBitsNative(VdpOutputSurface surface, void const *const *source_data,
                                  uint32_t const *source_pitches, VdpRect const *destination_rect)
{
    TRACE1("{zilch} VdpVideoMixerQueryParameterSupport");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpOutputSurfacePutBitsIndexed(VdpOutputSurface surface, VdpIndexedFormat source_indexed_format,
                                   void const *const *source_data, uint32_t const *source_pitch,
                                   VdpRect const *destination_rect,
                                   VdpColorTableFormat color_table_format, void const *color_table)
{
    TRACE1("{zilch} VdpOutputSurfacePutBitsIndexed");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpOutputSurfacePutBitsYCbCr(VdpOutputSurface surface, VdpYCbCrFormat source_ycbcr_format,
                                 void const *const *source_data, uint32_t const *source_pitches,
                                 VdpRect const *destination_rect, VdpCSCMatrix const *csc_matrix)
{
    TRACE1("{zilch} VdpOutputSurfacePutBitsYCbCr");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpVideoMixerQueryFeatureSupport(VdpDevice device, VdpVideoMixerFeature feature,
                                     VdpBool *is_supported)
{
    TRACE1("{zilch} VdpVideoMixerQueryFeatureSupport");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpVideoMixerQueryParameterSupport(VdpDevice device, VdpVideoMixerParameter parameter,
                                       VdpBool *is_supported)
{
    TRACE1("{zilch} VdpVideoMixerQueryParameterSupport");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpVideoMixerQueryAttributeSupport(VdpDevice device, VdpVideoMixerAttribute attribute,
                                       VdpBool *is_supported)
{
    TRACE1("{zilch} VdpVideoMixerQueryAttributeSupport");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpVideoMixerQueryParameterValueRange(VdpDevice device, VdpVideoMixerParameter parameter,
                                          void *min_value, void *max_value)
{
    TRACE1("{zilch} VdpVideoMixerQueryParameterValueRange");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpVideoMixerQueryAttributeValueRange(VdpDevice device, VdpVideoMixerAttribute attribute,
                                          void *min_value, void *max_value)
{
    TRACE1("{zilch} VdpVideoMixerQueryAttributeValueRange");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpVideoMixerCreate(VdpDevice device, uint32_t feature_count,
                        VdpVideoMixerFeature const *features, uint32_t parameter_count,
                        VdpVideoMixerParameter const *parameters,
                        void const *const *parameter_values, VdpVideoMixer *mixer)
{
    TRACE("{part} VdpVideoMixerCreate feature_count=%d, parameter_count=%d",
        feature_count, parameter_count);
#ifndef NDEBUG
    for (uint32_t k = 0; k < parameter_count; k ++) {
        printf("      parameter ");
        switch (parameters[k]) {
        case VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_WIDTH:
            printf("video surface width = %d\n", *(uint32_t*)parameter_values[k]);
            break;
        case VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_HEIGHT:
            printf("video surface height = %d\n", *(uint32_t*)parameter_values[k]);
            break;
        case VDP_VIDEO_MIXER_PARAMETER_CHROMA_TYPE:
            printf("chroma type = %s\n", reverse_chroma_type(*(uint32_t*)parameter_values[k]));
            break;
        case VDP_VIDEO_MIXER_PARAMETER_LAYERS:
            printf("layers = %d\n", *(uint32_t*)parameter_values[k]);
            break;
        default:
            return VDP_STATUS_INVALID_VIDEO_MIXER_PARAMETER;
        }
    }
#endif

    if (!handlestorage_valid(device, HANDLE_TYPE_DEVICE))
        return VDP_STATUS_INVALID_HANDLE;

    VdpVideoMixerData *data = (VdpVideoMixerData *)calloc(1, sizeof(VdpVideoMixerData));
    if (NULL == data)
        return VDP_STATUS_RESOURCES;

    data->type = HANDLE_TYPE_VIDEO_MIXER;
    data->device = device;

    *mixer = handlestorage_add(data);

    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpVideoMixerSetFeatureEnables(VdpVideoMixer mixer, uint32_t feature_count,
                                   VdpVideoMixerFeature const *features,
                                   VdpBool const *feature_enables)
{
    TRACE("{part} VdpVideoMixerSetFeatureEnables mixer=%d, feature_count=%d", mixer, feature_count);
    for (uint32_t k = 0; k < feature_count; k ++) {
        TRACE("   feature %d %s (%s)", features[k], feature_enables[k] ? "enabled" : "disabled",
            reverse_video_mixer_feature(features[k]));
    }
    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpVideoMixerSetAttributeValues(VdpVideoMixer mixer, uint32_t attribute_count,
                                    VdpVideoMixerAttribute const *attributes,
                                    void const *const *attribute_values)
{
    TRACE("{part} VdpVideoMixerSetAttributeValues mixer=%d, attribute_count=%d",
        mixer, attribute_count);
    for (uint32_t k = 0; k < attribute_count; k ++) {
        TRACE("   attribute %d (%s)", attributes[k], reverse_video_mixer_attributes(attributes[k]));
        if (VDP_VIDEO_MIXER_ATTRIBUTE_CSC_MATRIX == attributes[k]) {
            VdpCSCMatrix *matrix = (VdpCSCMatrix *)(attribute_values[k]);
            for (uint32_t j1 = 0; j1 < 3; j1 ++) {
                printf("      ");
                for (uint32_t j2 = 0; j2 < 4; j2 ++) {
                    printf("%11f", (double)((*matrix)[j1][j2]));
                }
                printf("\n");
            }
        }
    }

    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpVideoMixerGetFeatureSupport(VdpVideoMixer mixer, uint32_t feature_count,
                                   VdpVideoMixerFeature const *features, VdpBool *feature_supports)
{
    TRACE1("{zilch} VdpVideoMixerGetFeatureSupport");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpVideoMixerGetFeatureEnables(VdpVideoMixer mixer, uint32_t feature_count,
                                   VdpVideoMixerFeature const *features, VdpBool *feature_enables)
{
    TRACE1("{zilch} VdpVideoMixerGetFeatureEnables");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpVideoMixerGetParameterValues(VdpVideoMixer mixer, uint32_t parameter_count,
                                    VdpVideoMixerParameter const *parameters,
                                    void *const *parameter_values)
{
    TRACE1("{zilch} VdpVideoMixerGetParameterValues");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpVideoMixerGetAttributeValues(VdpVideoMixer mixer, uint32_t attribute_count,
                                    VdpVideoMixerAttribute const *attributes,
                                    void *const *attribute_values)
{
    TRACE1("{zilch} VdpVideoMixerGetAttributeValues");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpVideoMixerDestroy(VdpVideoMixer mixer)
{
    TRACE("{full} VdpVideoMixerDestroy mixer=%d", mixer);
    void *data = handlestorage_get(mixer, HANDLE_TYPE_VIDEO_MIXER);
    if (NULL == data)
        return VDP_STATUS_INVALID_HANDLE;

    free(data);
    handlestorage_expunge(mixer);

    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpVideoMixerRender(VdpVideoMixer mixer, VdpOutputSurface background_surface,
                        VdpRect const *background_source_rect,
                        VdpVideoMixerPictureStructure current_picture_structure,
                        uint32_t video_surface_past_count,
                        VdpVideoSurface const *video_surface_past,
                        VdpVideoSurface video_surface_current, uint32_t video_surface_future_count,
                        VdpVideoSurface const *video_surface_future,
                        VdpRect const *video_source_rect, VdpOutputSurface destination_surface,
                        VdpRect const *destination_rect, VdpRect const *destination_video_rect,
                        uint32_t layer_count, VdpLayer const *layers)
{
    TRACE1("{part} VdpVideoMixerRender");

#ifndef NDEBUG
    VdpRect const *rect;
    printf("      mixer=%d, background_surface=%d,", mixer, background_surface);
    printf(" background_source_rect=");

    rect = background_source_rect;
    if (NULL == rect) printf("NULL");
    else printf("(%d,%d,%d,%d)", rect->x0, rect->y0, rect->x1, rect->y1);

    printf(",\n      current_picture_structure=%s,\n",
        reverser_video_mixer_picture_structure(current_picture_structure));
    printf("     ");
    if (video_surface_past_count > 0) {
        printf(" video_surface_past=[");
        for (uint32_t k = 0; k < video_surface_past_count; k ++) {
            if (0 != k) printf(",");
            printf("%d", video_surface_past[k]);
        }
        printf("],");
    } else {
        printf(" no_video_surface_past,");
    }
    printf(" video_surface_current=%d,", video_surface_current);
    if (video_surface_future_count > 0) {
        printf(" video_surface_future=[");
        for (uint32_t k = 0; k < video_surface_future_count; k ++) {
            if (0 != k) printf(",");
            printf("%d", video_surface_future[k]);
        }
        printf("],");
    } else {
        printf(" no_video_surface_future,");
    }

    printf("\n      video_source_rect=");
    rect = video_source_rect;
    if (NULL == rect) printf("NULL");
    else printf("(%d,%d,%d,%d)", rect->x0, rect->y0, rect->x1, rect->y1);

    printf(", destination_surface=%d,", destination_surface);

    printf("\n      destination_rect=");
    rect = destination_rect;
    if (NULL == rect) printf("NULL");
    else printf("(%d,%d,%d,%d)", rect->x0, rect->y0, rect->x1, rect->y1);

    printf(", video_surface_current=%d,", video_surface_current);

    if (layer_count > 0) {
        printf(" layers=[");
        for (uint32_t k = 0; k < layer_count; k ++) {
            if (0 != k) printf(",");
            printf("{%d,", layers[k].source_surface);
            rect = layers[k].source_rect;
            if (NULL == rect) printf("src:NULL,");
            else printf("src:[%d,%d,%d,%d],", rect->x0, rect->y0, rect->x1, rect->y1);
            rect = layers[k].destination_rect;
            if (NULL == rect) printf("dst:NULL,");
            else printf("dst:[%d,%d,%d,%d],", rect->x0, rect->y0, rect->x1, rect->y1);
            printf("}");
        }
        printf("]");
    } else {
        printf(" no_layers");
    }

    printf("\n");
#endif

    //TODO: handle rectangles

    VdpVideoSurfaceData *source_surface =
        handlestorage_get(video_surface_current, HANDLE_TYPE_VIDEO_SURFACE);
    if (NULL == source_surface)
        return VDP_STATUS_INVALID_HANDLE;

    VdpOutputSurfaceData *dest_surface =
        handlestorage_get(destination_surface, HANDLE_TYPE_OUTPUT_SURFACE);
    if (NULL == dest_surface)
        return VDP_STATUS_INVALID_HANDLE;

    struct SwsContext *sws_ctx =
        sws_getContext(source_surface->width, source_surface->height, PIX_FMT_YUV420P,
            dest_surface->width, dest_surface->height, PIX_FMT_RGBA,
            SWS_POINT, NULL, NULL, NULL);

    uint8_t const * const src_planes[] =
        { source_surface->y_plane, source_surface->v_plane, source_surface->u_plane, NULL };
    int src_strides[] =
        {source_surface->stride, source_surface->width/2, source_surface->width/2, 0};
    uint8_t *dst_planes[] = {dest_surface->buf, NULL, NULL, NULL};
    int dst_strides[] = {dest_surface->stride * rgba_format_storage_size(dest_surface->rgba_format),
                         0, 0, 0};
    int res = sws_scale(sws_ctx,
                        src_planes, src_strides, 0, source_surface->height,
                        dst_planes, dst_strides);

    sws_freeContext(sws_ctx);

    // printf ("res = %d, while height = %d\n", res, source_surface->height);
    if (res != source_surface->height)
        return VDP_STATUS_ERROR;

    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpPresentationQueueTargetDestroy(VdpPresentationQueueTarget presentation_queue_target)
{
    TRACE("{full} VdpPresentationQueueTargetDestroy presentation_queue_target=%d",
        presentation_queue_target);
    void *data = handlestorage_get(presentation_queue_target, HANDLE_TYPE_PRESENTATION_QUEUE_TARGET);
    if (NULL == data)
        return VDP_STATUS_INVALID_HANDLE;

    free(data);
    handlestorage_expunge(presentation_queue_target);
    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpPresentationQueueCreate(VdpDevice device,
                               VdpPresentationQueueTarget presentation_queue_target,
                               VdpPresentationQueue *presentation_queue)
{
    TRACE("{part} VdpPresentationQueueCreate device=%d, presentation_queue_target=%d",
        device, presentation_queue_target);
    VdpPresentationQueueData *data =
        (VdpPresentationQueueData *)calloc(1, sizeof(VdpPresentationQueueData));
    if (NULL == data)
        return VDP_STATUS_RESOURCES;
    if (!handlestorage_valid(device, HANDLE_TYPE_DEVICE))
        return VDP_STATUS_INVALID_HANDLE;
    if (!handlestorage_valid(presentation_queue_target, HANDLE_TYPE_PRESENTATION_QUEUE_TARGET))
        return VDP_STATUS_INVALID_HANDLE;

    data->type = HANDLE_TYPE_PRESENTATION_QUEUE;
    data->device = device;
    data->presentation_queue_target = presentation_queue_target;

    *presentation_queue = handlestorage_add(data);

    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpPresentationQueueDestroy(VdpPresentationQueue presentation_queue)
{
    TRACE("{full} VdpPresentationQueueDestroy presentation_queue=%d", presentation_queue);
    void *data = handlestorage_get(presentation_queue, HANDLE_TYPE_PRESENTATION_QUEUE);
    if (NULL == data)
        return VDP_STATUS_INVALID_HANDLE;

    free(data);
    handlestorage_expunge(presentation_queue);
    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpPresentationQueueSetBackgroundColor(VdpPresentationQueue presentation_queue,
                                           VdpColor *const background_color)
{
    TRACE1("{zilch} VdpPresentationQueueSetBackgroundColor");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpPresentationQueueGetBackgroundColor(VdpPresentationQueue presentation_queue,
                                           VdpColor *background_color)
{
    TRACE1("{zilch} VdpPresentationQueueGetBackgroundColor");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpPresentationQueueGetTime(VdpPresentationQueue presentation_queue,
                                VdpTime *current_time)
{
    TRACE("{full} VdpPresentationQueueGetTime presentation_queue=%d", presentation_queue);
    struct timeval tv;
    gettimeofday(&tv, NULL);
    *current_time = (uint64_t)tv.tv_sec * 1000000000LL + (uint64_t)tv.tv_usec * 1000LL;
    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpPresentationQueueDisplay(VdpPresentationQueue presentation_queue, VdpOutputSurface surface,
                                uint32_t clip_width, uint32_t clip_height,
                                VdpTime earliest_presentation_time)
{
    TRACE("{part} VdpPresentationQueueDisplay presentation_queue=%d, surface=%d, "
        "clip_width=%d, clip_height=%d", presentation_queue, surface, clip_width, clip_height);

    VdpOutputSurfaceData *surfaceData = handlestorage_get(surface, HANDLE_TYPE_OUTPUT_SURFACE);
    if (NULL == surfaceData) return VDP_STATUS_INVALID_HANDLE;

    VdpPresentationQueueData *presentationQueueData =
        handlestorage_get(presentation_queue, HANDLE_TYPE_PRESENTATION_QUEUE);

    if (NULL == presentationQueueData) return VDP_STATUS_INVALID_HANDLE;

    VdpPresentationQueueTargetData *presentationQueueTargetData =
        handlestorage_get(presentationQueueData->presentation_queue_target,
        HANDLE_TYPE_PRESENTATION_QUEUE_TARGET);

    if (NULL == presentationQueueTargetData) return VDP_STATUS_INVALID_HANDLE;

    Drawable drawable = presentationQueueTargetData->drawable;

    VdpDeviceData *deviceData =
        handlestorage_get(presentationQueueTargetData->device, HANDLE_TYPE_DEVICE);

    if (NULL == deviceData) return VDP_STATUS_INVALID_HANDLE;

    Display *display = deviceData->display;
    int screen = deviceData->screen;

    XImage *image = XCreateImage(display, DefaultVisual(display, screen), 24, ZPixmap, 0,
        (char *)(surfaceData->buf), surfaceData->width, surfaceData->height, 32,
        surfaceData->stride * rgba_format_storage_size(surfaceData->rgba_format));

    if (NULL == image) {
        TRACE1("image become NULL in VdpPresentationQueueDisplay");
        return VDP_STATUS_RESOURCES;
    }

    XPutImage(display, drawable, DefaultGC(display, screen), image, 0, 0, 0, 0,
        surfaceData->width, surfaceData->height);

    free(image);

    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpPresentationQueueBlockUntilSurfaceIdle(VdpPresentationQueue presentation_queue,
                                              VdpOutputSurface surface,
                                              VdpTime *first_presentation_time)

{
    TRACE("{full} VdpPresentationQueueBlockUntilSurfaceIdle presentation_queue=%d, surface=%d",
        presentation_queue, surface);

    if (! handlestorage_valid(presentation_queue, HANDLE_TYPE_PRESENTATION_QUEUE))
        return VDP_STATUS_INVALID_HANDLE;

    if (! handlestorage_valid(surface, HANDLE_TYPE_OUTPUT_SURFACE))
        return VDP_STATUS_INVALID_HANDLE;

    // use current time as presentation time
    fakeVdpPresentationQueueGetTime(presentation_queue, first_presentation_time);

    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpPresentationQueueQuerySurfaceStatus(VdpPresentationQueue presentation_queue,
                                           VdpOutputSurface surface,
                                           VdpPresentationQueueStatus *status,
                                           VdpTime *first_presentation_time)
{
    *status = VDP_PRESENTATION_QUEUE_STATUS_VISIBLE;
    TRACE1("{part} VdpPresentationQueueQuerySurfaceStatus");
    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpVideoSurfaceQueryCapabilities(VdpDevice device, VdpChromaType surface_chroma_type,
                                     VdpBool *is_supported, uint32_t *max_width,
                                     uint32_t *max_height)
{
    TRACE1("{zilch} VdpVideoSurfaceQueryCapabilities");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpVideoSurfaceQueryGetPutBitsYCbCrCapabilities(VdpDevice device,
                                                    VdpChromaType surface_chroma_type,
                                                    VdpYCbCrFormat bits_ycbcr_format,
                                                    VdpBool *is_supported)
{
    TRACE1("{zilch} VdpVideoSurfaceQueryGetPutBitsYCbCrCapabilities");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpVideoSurfaceCreate(VdpDevice device, VdpChromaType chroma_type, uint32_t width,
                          uint32_t height, VdpVideoSurface *surface)
{
    TRACE("{part} VdpVideoSurfaceCreate, device=%d, chroma_type=%s, width=%d, height=%d",
        device, reverse_chroma_type(chroma_type), width, height);

    if (! handlestorage_valid(device, HANDLE_TYPE_DEVICE))
        return VDP_STATUS_INVALID_HANDLE;

    VdpVideoSurfaceData *data = (VdpVideoSurfaceData *)calloc(1, sizeof(VdpVideoSurfaceData));
    if (NULL == data)
        return VDP_STATUS_RESOURCES;

    uint32_t const stride = (width % 4 == 0) ? width : (width & ~0x3UL) + 4;

    data->type = HANDLE_TYPE_VIDEO_SURFACE;
    data->device = device;
    data->chroma_type = chroma_type;
    data->width = width;
    data->stride = stride;
    data->height = height;
    //TODO: find valid storage size for chroma_type
    data->y_plane = malloc(stride * height);
    data->v_plane = malloc(stride * height / chroma_storage_size_divider(chroma_type));
    data->u_plane = malloc(stride * height / chroma_storage_size_divider(chroma_type));
    if (NULL == data->y_plane || NULL == data->v_plane || NULL == data->u_plane) {
        if (data->y_plane) free(data->y_plane);
        if (data->v_plane) free(data->v_plane);
        if (data->u_plane) free(data->u_plane);
        free(data);
        return VDP_STATUS_RESOURCES;
    }

    *surface = handlestorage_add(data);

    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpVideoSurfaceDestroy(VdpVideoSurface surface)
{
    TRACE("{full} VdpVideoSurfaceDestroy surface=%d", surface);

    VdpVideoSurfaceData *data = handlestorage_get(surface, HANDLE_TYPE_VIDEO_SURFACE);
    if (NULL == data)
        return VDP_STATUS_INVALID_HANDLE;

    free(data->y_plane);
    free(data->v_plane);
    free(data->u_plane);
    free(data);
    handlestorage_expunge(surface);
    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpVideoSurfaceGetParameters(VdpVideoSurface surface, VdpChromaType *chroma_type,
                                 uint32_t *width, uint32_t *height)
{
    TRACE1("{zilch} VdpVideoSurfaceGetParameters");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpVideoSurfaceGetBitsYCbCr(VdpVideoSurface surface, VdpYCbCrFormat destination_ycbcr_format,
                                void *const *destination_data, uint32_t const *destination_pitches)
{
    TRACE1("{zilch} VdpVideoSurfaceGetBitsYCbCr");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpVideoSurfacePutBitsYCbCr(VdpVideoSurface surface, VdpYCbCrFormat source_ycbcr_format,
                                void const *const *source_data, uint32_t const *source_pitches)
{
    TRACE("{part} VdpVideoSurfacePutBitsYCbCr surface=%d, source_ycbcr_format=%s",
        surface, reverse_ycbcr_format(source_ycbcr_format));

    //TODO: figure out what to do with other formats
    if (VDP_YCBCR_FORMAT_YV12 != source_ycbcr_format)
        return VDP_STATUS_INVALID_Y_CB_CR_FORMAT;

    VdpVideoSurfaceData *surfaceData = handlestorage_get(surface, HANDLE_TYPE_VIDEO_SURFACE);
    if (NULL == surfaceData)
        return VDP_STATUS_INVALID_HANDLE;

    uint8_t const *src;
    uint8_t *dst;
    dst = surfaceData->y_plane;     src = source_data[0];
    for (uint32_t k = 0; k < surfaceData->height; k ++) {
        memcpy(dst, src, surfaceData->width);
        dst += surfaceData->stride;
        src += source_pitches[0];
    }

    dst = surfaceData->v_plane;     src = source_data[1];
    for (uint32_t k = 0; k < surfaceData->height / 2; k ++) {
        memcpy(dst, src, surfaceData->width / 2);
        dst += surfaceData->width / 2;
        src += source_pitches[1];
    }

    dst = surfaceData->u_plane;     src = source_data[2];
    for (uint32_t k = 0; k < surfaceData->height / 2; k ++) {
        memcpy(dst, src, surfaceData->width/2);
        dst += surfaceData->width / 2;
        src += source_pitches[2];
    }

    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpBitmapSurfaceQueryCapabilities(VdpDevice device, VdpRGBAFormat surface_rgba_format,
                                      VdpBool *is_supported, uint32_t *max_width,
                                      uint32_t *max_height)
{
    TRACE("{part} VdpBitmapSurfaceQueryCapabilities device=%d", device);
    if (! handlestorage_valid(device, HANDLE_TYPE_DEVICE))
        return VDP_STATUS_INVALID_HANDLE;

    printf("      format %s\n", reverse_rgba_format(surface_rgba_format));

    *is_supported = 1;
    *max_width = 2048;
    *max_height = 2048;

    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpBitmapSurfaceCreate(VdpDevice device, VdpRGBAFormat rgba_format, uint32_t width,
                           uint32_t height, VdpBool frequently_accessed, VdpBitmapSurface *surface)
{
    TRACE("{full} VdpBitmapSurfaceCreate device=%d, rgba_format=%s, width=%d, height=%d,"
        "frequently_accessed=%d", device, reverse_rgba_format(rgba_format), width, height,
        frequently_accessed);
    VdpDeviceData *deviceData = handlestorage_get(device, HANDLE_TYPE_DEVICE);
    if (NULL == deviceData)
        return VDP_STATUS_INVALID_HANDLE;

    VdpBitmapSurfaceData *data = (VdpBitmapSurfaceData *)calloc(1, sizeof(VdpBitmapSurfaceData));
    if (NULL == data)
        return VDP_STATUS_RESOURCES;

    uint32_t const stride = (width % 4 == 0) ? width : (width & ~0x3UL) + 4;
    void *buf = malloc(stride * height * rgba_format_storage_size(rgba_format));
    if (NULL == buf) {
        free(data);
        return VDP_STATUS_RESOURCES;
    }

    data->type = HANDLE_TYPE_BITMAP_SURFACE;
    data->device = device;
    data->rgba_format = rgba_format;
    data->width = width;
    data->height = height;
    data->stride = stride;
    data->buf = buf;

    *surface = handlestorage_add(data);
    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpBitmapSurfaceDestroy(VdpBitmapSurface surface)
{
    TRACE("{full} VdpBitmapSurfaceDestroy surface=%d", surface);
    VdpBitmapSurfaceData *data = handlestorage_get(surface, HANDLE_TYPE_BITMAP_SURFACE);
    if (NULL == data)
        return VDP_STATUS_INVALID_HANDLE;

    free(data->buf);
    free(data);
    handlestorage_expunge(surface);
    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpBitmapSurfaceGetParameters(VdpBitmapSurface surface, VdpRGBAFormat *rgba_format,
                                  uint32_t *width, uint32_t *height, VdpBool *frequently_accessed)
{
    TRACE1("{zilch} VdpBitmapSurfaceGetParameters");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpBitmapSurfacePutBitsNative(VdpBitmapSurface surface, void const *const *source_data,
                                  uint32_t const *source_pitches, VdpRect const *destination_rect)
{
    TRACE("{part} VdpBitmapSurfacePutBitsNative surface=%d", surface);
#ifndef NDEBUG
    printf("      destination_rect=");
    if (NULL == destination_rect) printf("NULL");
    else printf("(%d,%d,%d,%d)", destination_rect->x0, destination_rect->y0,
        destination_rect->x1, destination_rect->y1);
    printf("\n");
#endif

    VdpBitmapSurfaceData *surfaceData = handlestorage_get(surface, HANDLE_TYPE_BITMAP_SURFACE);
    if (NULL == surfaceData)
        return VDP_STATUS_INVALID_HANDLE;

    //TODO: fix handling other formats
    if (VDP_RGBA_FORMAT_B8G8R8A8 != surfaceData->rgba_format)
        return VDP_STATUS_INVALID_RGBA_FORMAT;

    VdpRect rect = {0, 0, surfaceData->width, surfaceData->height};
    if (NULL != destination_rect) rect = *destination_rect;

    for (uint32_t line = rect.y0; line < rect.y1; line ++) {
        uint8_t *dst = (uint8_t *)surfaceData->buf + (rect.x0 + line*surfaceData->stride) * 4;
        uint8_t *src = (uint8_t *)(source_data[0]) + (line - rect.y0) * source_pitches[0];
        memcpy(dst, src, 4*(rect.x1 - rect.x0));
    }

    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpDeviceDestroy(VdpDevice device)
{
    TRACE("{full} VdpDeviceDestroy device=%d", device);
    void *data = handlestorage_get(device, HANDLE_TYPE_DEVICE);
    if (NULL == data)
        return VDP_STATUS_INVALID_HANDLE;

    free(data);
    handlestorage_expunge(device);
    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpGetInformationString(char const **information_string)
{
    TRACE1("{full} VdpGetInformationString");
    *information_string = implemetation_description_string;
    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpGenerateCSCMatrix(VdpProcamp *procamp, VdpColorStandard standard, VdpCSCMatrix *csc_matrix)
{
    TRACE("{part} VdpGenerateCSCMatrix standard=%d", standard);
    if (NULL == procamp || NULL == csc_matrix)
        return VDP_STATUS_INVALID_POINTER;
    if (VDP_PROCAMP_VERSION != procamp->struct_version)
        return VDP_STATUS_INVALID_VALUE;

    VdpCSCMatrix *m = csc_matrix;
    switch (standard) {
    case VDP_COLOR_STANDARD_ITUR_BT_601:
        (*m)[0][0] = 1.16438f; (*m)[0][1] =  0.0f;     (*m)[0][2] =  1.59603f; (*m)[0][3] = -222.921f;
        (*m)[1][0] = 1.16438f; (*m)[1][1] = -0.39176f; (*m)[1][2] = -0.81297f; (*m)[1][3] =  135.576f;
        (*m)[2][0] = 1.16438f; (*m)[2][1] =  2.01723f; (*m)[2][2] =  0.0f;     (*m)[2][3] = -276.836f;
        break;
    case VDP_COLOR_STANDARD_ITUR_BT_709:
        (*m)[0][0] =  1.0f; (*m)[0][1] =  0.0f;     (*m)[0][2] =  1.402f;   (*m)[0][3] = -179.456f;
        (*m)[1][0] =  1.0f; (*m)[1][1] = -0.34414f; (*m)[1][2] = -0.71414f; (*m)[1][3] =  135.460f;
        (*m)[2][0] =  1.0f; (*m)[2][1] =  1.772f;   (*m)[2][2] =  0.0f;     (*m)[2][3] = -226.816f;
        break;
    case VDP_COLOR_STANDARD_SMPTE_240M:
        (*m)[0][0] =  0.58139f; (*m)[0][1] = -0.76437f; (*m)[0][2] =  1.5760f;  (*m)[0][3] = 0.0f;
        (*m)[1][0] =  0.58140f; (*m)[1][1] = -0.99101f; (*m)[1][2] = -0.47663f; (*m)[1][3] = 0.0f;
        (*m)[2][0] =  0.58139f; (*m)[2][1] =  1.0616f;  (*m)[2][2] =  0.00000f; (*m)[2][3] = 0.0f;
        break;
    default:
        return VDP_STATUS_INVALID_COLOR_STANDARD;
    }

    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpOutputSurfaceRenderOutputSurface(VdpOutputSurface destination_surface,
                                        VdpRect const *destination_rect,
                                        VdpOutputSurface source_surface, VdpRect const *source_rect,
                                        VdpColor const *colors,
                                        VdpOutputSurfaceRenderBlendState const *blend_state,
                                        uint32_t flags)
{
    TRACE("{dirty impl} VdpOutputSurfaceRenderOutputSurface destination_surface=%d, source_surface=%d",
        destination_surface, source_surface);
#ifndef NDEBUG
    printf("      destination_rect=");
    if (NULL == destination_rect) printf("NULL");
    else printf("(%d,%d,%d,%d)",    destination_rect->x0, destination_rect->y0,
                                    destination_rect->x1, destination_rect->y1);

    printf(", source_rect=");
    if (NULL == source_rect) printf("NULL");
    else printf("(%d,%d,%d,%d)", source_rect->x0, source_rect->y0, source_rect->x1, source_rect->y1);
    printf("\n      colors=%p, blend_state=%p, flags=%d", colors, blend_state, flags);
    printf("\n");
#endif

    //TODO: remove code duplication with fakeVdpOutputSurfaceRenderBitmapSurface
    //TODO: use swscale
    VdpOutputSurfaceData *dstSurface =
        handlestorage_get(destination_surface, HANDLE_TYPE_OUTPUT_SURFACE);
    if (NULL == dstSurface)
        return VDP_STATUS_INVALID_HANDLE;

    VdpOutputSurfaceData *srcSurface =
        handlestorage_get(source_surface, HANDLE_TYPE_OUTPUT_SURFACE);
    if (NULL == srcSurface)
        return VDP_STATUS_INVALID_HANDLE;

    memcpy(dstSurface->buf, srcSurface->buf, srcSurface->stride * srcSurface->height * 4);

    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpOutputSurfaceRenderBitmapSurface(VdpOutputSurface destination_surface,
                                        VdpRect const *destination_rect,
                                        VdpBitmapSurface source_surface, VdpRect const *source_rect,
                                        VdpColor const *colors,
                                        VdpOutputSurfaceRenderBlendState const *blend_state,
                                        uint32_t flags)
{
    TRACE("{dirty impl} VdpOutputSurfaceRenderBitmapSurface destination_surface=%d, source_surface=%d",
        destination_surface, source_surface);
#ifndef NDEBUG
    printf("      destination_rect=");
    if (NULL == destination_rect) printf("NULL");
    else printf("(%d,%d,%d,%d)",    destination_rect->x0, destination_rect->y0,
                                    destination_rect->x1, destination_rect->y1);

    printf(", source_rect=");
    if (NULL == source_rect) printf("NULL");
    else printf("(%d,%d,%d,%d)", source_rect->x0, source_rect->y0, source_rect->x1, source_rect->y1);
    printf("\n      colors=%p, blend_state=%p, flags=%d", colors, blend_state, flags);
    printf("\n");
#endif

    //TODO: stop doing dumb things and use swscale instead
    VdpOutputSurfaceData *dstSurface =
        handlestorage_get(destination_surface, HANDLE_TYPE_OUTPUT_SURFACE);
    if (NULL == dstSurface)
        return VDP_STATUS_INVALID_HANDLE;

    VdpBitmapSurfaceData *srcSurface =
        handlestorage_get(source_surface, HANDLE_TYPE_BITMAP_SURFACE);
    if (NULL == srcSurface)
        return VDP_STATUS_INVALID_HANDLE;
<commit_msg>print blend_state struct and check its version in VdpOutputSurfaceRenderBitmapSurface<commit_after>#include <vdpau/vdpau.h>
#include <vdpau/vdpau_x11.h>
#include <stdio.h>
#include <assert.h>
#include <glib.h>
#include <libswscale/swscale.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include "reverse-constant.h"
#include "handle-storage.h"

#ifndef NDEBUG
    #define TRACE(str, ...)  printf("[VDPFAKE] " str "\n", __VA_ARGS__)
    #define TRACE1(str)  printf("[VDPFAKE] " str "\n")
#else
    #define TRACE(str, ...)
    #define TRACE1(str)
#endif

char const *implemetation_description_string = "VAAPI/software backend for VDPAU";

static
uint32_t
rgba_format_storage_size(VdpRGBAFormat rgba_format)
{
    switch (rgba_format) {
    case VDP_RGBA_FORMAT_B8G8R8A8:
    case VDP_RGBA_FORMAT_R8G8B8A8:
    case VDP_RGBA_FORMAT_R10G10B10A2:
    case VDP_RGBA_FORMAT_B10G10R10A2:
        return 4;
    case VDP_RGBA_FORMAT_A8:
        return 1;
    default:
        return 4;
    }
}

static
uint32_t
chroma_storage_size_divider(VdpChromaType chroma_type)
{
    switch (chroma_type) {
    case VDP_CHROMA_TYPE_420: return 4;
    case VDP_CHROMA_TYPE_422: return 2;
    case VDP_CHROMA_TYPE_444: return 1;
    default: return 1;
    }
}

static
const char *
fakeVdpGetErrorString(VdpStatus status)
{
    switch (status) {
    case VDP_STATUS_OK:
        return "VDP_STATUS_OK";
        break;
    case VDP_STATUS_NO_IMPLEMENTATION:
        return "VDP_STATUS_NO_IMPLEMENTATION";
        break;
    case VDP_STATUS_DISPLAY_PREEMPTED:
        return "VDP_STATUS_DISPLAY_PREEMPTED";
        break;
    case VDP_STATUS_INVALID_HANDLE:
        return "VDP_STATUS_INVALID_HANDLE";
        break;
    case VDP_STATUS_INVALID_POINTER:
        return "VDP_STATUS_INVALID_POINTER";
        break;
    case VDP_STATUS_INVALID_CHROMA_TYPE:
        return "VDP_STATUS_INVALID_CHROMA_TYPE";
        break;
    case VDP_STATUS_INVALID_Y_CB_CR_FORMAT:
        return "VDP_STATUS_INVALID_Y_CB_CR_FORMAT";
        break;
    case VDP_STATUS_INVALID_RGBA_FORMAT:
        return "VDP_STATUS_INVALID_RGBA_FORMAT";
        break;
    case VDP_STATUS_INVALID_INDEXED_FORMAT:
        return "VDP_STATUS_INVALID_INDEXED_FORMAT";
        break;
    case VDP_STATUS_INVALID_COLOR_STANDARD:
        return "VDP_STATUS_INVALID_COLOR_STANDARD";
        break;
    case VDP_STATUS_INVALID_COLOR_TABLE_FORMAT:
        return "VDP_STATUS_INVALID_COLOR_TABLE_FORMAT";
        break;
    case VDP_STATUS_INVALID_BLEND_FACTOR:
        return "VDP_STATUS_INVALID_BLEND_FACTOR";
        break;
    case VDP_STATUS_INVALID_BLEND_EQUATION:
        return "VDP_STATUS_INVALID_BLEND_EQUATION";
        break;
    case VDP_STATUS_INVALID_FLAG:
        return "VDP_STATUS_INVALID_FLAG";
        break;
    case VDP_STATUS_INVALID_DECODER_PROFILE:
        return "VDP_STATUS_INVALID_DECODER_PROFILE";
        break;
    case VDP_STATUS_INVALID_VIDEO_MIXER_FEATURE:
        return "VDP_STATUS_INVALID_VIDEO_MIXER_FEATURE";
        break;
    case VDP_STATUS_INVALID_VIDEO_MIXER_PARAMETER:
        return "VDP_STATUS_INVALID_VIDEO_MIXER_PARAMETER";
        break;
    case VDP_STATUS_INVALID_VIDEO_MIXER_ATTRIBUTE:
        return "VDP_STATUS_INVALID_VIDEO_MIXER_ATTRIBUTE";
        break;
    case VDP_STATUS_INVALID_VIDEO_MIXER_PICTURE_STRUCTURE:
        return "VDP_STATUS_INVALID_VIDEO_MIXER_PICTURE_STRUCTURE";
        break;
    case VDP_STATUS_INVALID_FUNC_ID:
        return "VDP_STATUS_INVALID_FUNC_ID";
        break;
    case VDP_STATUS_INVALID_SIZE:
        return "VDP_STATUS_INVALID_SIZE";
        break;
    case VDP_STATUS_INVALID_VALUE:
        return "VDP_STATUS_INVALID_VALUE";
        break;
    case VDP_STATUS_INVALID_STRUCT_VERSION:
        return "VDP_STATUS_INVALID_STRUCT_VERSION";
        break;
    case VDP_STATUS_RESOURCES:
        return "VDP_STATUS_RESOURCES";
        break;
    case VDP_STATUS_HANDLE_DEVICE_MISMATCH:
        return "VDP_STATUS_HANDLE_DEVICE_MISMATCH";
        break;
    case VDP_STATUS_ERROR:
        return "VDP_STATUS_ERROR";
        break;
    default:
        return "VDP Unknown error";
        break;
    } // switch
}

static
VdpStatus
fakeVdpGetApiVersion(uint32_t *api_version)
{
    TRACE1("{full} VdpGetApiVersion");
    *api_version = VDPAU_VERSION;
    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpDecoderQueryCapabilities(VdpDevice device, VdpDecoderProfile profile, VdpBool *is_supported,
                                uint32_t *max_level, uint32_t *max_macroblocks,
                                uint32_t *max_width, uint32_t *max_height)
{
    TRACE1("{zilch} VdpDecoderQueryCapabilities");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpDecoderCreate(VdpDevice device, VdpDecoderProfile profile, uint32_t width, uint32_t height,
                     uint32_t max_references, VdpDecoder *decoder)
{
    TRACE1("{zilch} VdpDecoderCreate");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpDecoderDestroy(VdpDecoder decoder)
{
    TRACE1("{zilch} VdpDecoderDestroy");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpDecoderGetParameters(VdpDecoder decoder, VdpDecoderProfile *profile,
                            uint32_t *width, uint32_t *height)
{
    TRACE1("{zilch} VdpDecoderGetParameters");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpDecoderRender(VdpDecoder decoder, VdpVideoSurface target,
                     VdpPictureInfo const *picture_info, uint32_t bitstream_buffer_count,
                     VdpBitstreamBuffer const *bitstream_buffers)
{
    TRACE1("{zilch} VdpDecoderRender");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpOutputSurfaceQueryCapabilities(VdpDevice device, VdpRGBAFormat surface_rgba_format,
                                      VdpBool *is_supported, uint32_t *max_width,
                                      uint32_t *max_height)
{
    TRACE1("{zilch} VdpOutputSurfaceQueryCapabilities");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpOutputSurfaceQueryGetPutBitsNativeCapabilities(VdpDevice device,
                                                      VdpRGBAFormat surface_rgba_format,
                                                      VdpBool *is_supported)
{
    TRACE1("{zilch} VdpOutputSurfaceQueryGetPutBitsNativeCapabilities");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpOutputSurfaceQueryPutBitsIndexedCapabilities(VdpDevice device,
                                                    VdpRGBAFormat surface_rgba_format,
                                                    VdpIndexedFormat bits_indexed_format,
                                                    VdpColorTableFormat color_table_format,
                                                    VdpBool *is_supported)
{
    TRACE1("{zilch} VdpOutputSurfaceQueryPutBitsIndexedCapabilities");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpOutputSurfaceQueryPutBitsYCbCrCapabilities(VdpDevice device,
                                                  VdpRGBAFormat surface_rgba_format,
                                                  VdpYCbCrFormat bits_ycbcr_format,
                                                  VdpBool *is_supported)
{
    TRACE1("{zilch} VdpOutputSurfaceQueryPutBitsYCbCrCapabilities");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpOutputSurfaceCreate(VdpDevice device, VdpRGBAFormat rgba_format, uint32_t width,
                           uint32_t height, VdpOutputSurface *surface)
{
    TRACE("{part} VdpOutputSurfaceCreate device=%d, rgba_format=%s, width=%d, height=%d",
        device, reverse_rgba_format(rgba_format), width, height);
    if (! handlestorage_valid(device, HANDLE_TYPE_DEVICE))
        return VDP_STATUS_INVALID_HANDLE;

    if (width > 4096 || height > 4096)
        return VDP_STATUS_INVALID_SIZE;

    VdpOutputSurfaceData *data = (VdpOutputSurfaceData *)calloc(1, sizeof(VdpOutputSurfaceData));
    if (NULL == data)
        return VDP_STATUS_RESOURCES;

    uint32_t const stride = (width % 4 == 0) ? width : (width & ~0x3UL) + 4;

    data->type = HANDLE_TYPE_OUTPUT_SURFACE;
    data->device = device;
    data->rgba_format = rgba_format;
    data->width = width;
    data->stride = stride;
    data->height = height;
    data->buf = malloc(stride * height * rgba_format_storage_size(rgba_format));

    if (NULL == data->buf) {
        free(data);
        return VDP_STATUS_RESOURCES;
    }

    *surface = handlestorage_add(data);

    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpOutputSurfaceDestroy(VdpOutputSurface surface)
{
    TRACE("{full} VdpOutputSurfaceDestroy surface=%d", surface);
    VdpOutputSurfaceData *data = handlestorage_get(surface, HANDLE_TYPE_OUTPUT_SURFACE);
    if (NULL == data)
        return VDP_STATUS_INVALID_HANDLE;

    free(data->buf);
    free(data);
    handlestorage_expunge(surface);
    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpOutputSurfaceGetParameters(VdpOutputSurface surface, VdpRGBAFormat *rgba_format,
                                  uint32_t *width, uint32_t *height)
{
    TRACE1("{zilch} VdpOutputSurfaceGetParameters");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpOutputSurfaceGetBitsNative(VdpOutputSurface surface, VdpRect const *source_rect,
                                  void *const *destination_data,
                                  uint32_t const *destination_pitches)
{
    TRACE1("{zilch} VdpOutputSurfaceGetBitsNative");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpOutputSurfacePutBitsNative(VdpOutputSurface surface, void const *const *source_data,
                                  uint32_t const *source_pitches, VdpRect const *destination_rect)
{
    TRACE1("{zilch} VdpVideoMixerQueryParameterSupport");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpOutputSurfacePutBitsIndexed(VdpOutputSurface surface, VdpIndexedFormat source_indexed_format,
                                   void const *const *source_data, uint32_t const *source_pitch,
                                   VdpRect const *destination_rect,
                                   VdpColorTableFormat color_table_format, void const *color_table)
{
    TRACE1("{zilch} VdpOutputSurfacePutBitsIndexed");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpOutputSurfacePutBitsYCbCr(VdpOutputSurface surface, VdpYCbCrFormat source_ycbcr_format,
                                 void const *const *source_data, uint32_t const *source_pitches,
                                 VdpRect const *destination_rect, VdpCSCMatrix const *csc_matrix)
{
    TRACE1("{zilch} VdpOutputSurfacePutBitsYCbCr");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpVideoMixerQueryFeatureSupport(VdpDevice device, VdpVideoMixerFeature feature,
                                     VdpBool *is_supported)
{
    TRACE1("{zilch} VdpVideoMixerQueryFeatureSupport");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpVideoMixerQueryParameterSupport(VdpDevice device, VdpVideoMixerParameter parameter,
                                       VdpBool *is_supported)
{
    TRACE1("{zilch} VdpVideoMixerQueryParameterSupport");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpVideoMixerQueryAttributeSupport(VdpDevice device, VdpVideoMixerAttribute attribute,
                                       VdpBool *is_supported)
{
    TRACE1("{zilch} VdpVideoMixerQueryAttributeSupport");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpVideoMixerQueryParameterValueRange(VdpDevice device, VdpVideoMixerParameter parameter,
                                          void *min_value, void *max_value)
{
    TRACE1("{zilch} VdpVideoMixerQueryParameterValueRange");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpVideoMixerQueryAttributeValueRange(VdpDevice device, VdpVideoMixerAttribute attribute,
                                          void *min_value, void *max_value)
{
    TRACE1("{zilch} VdpVideoMixerQueryAttributeValueRange");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpVideoMixerCreate(VdpDevice device, uint32_t feature_count,
                        VdpVideoMixerFeature const *features, uint32_t parameter_count,
                        VdpVideoMixerParameter const *parameters,
                        void const *const *parameter_values, VdpVideoMixer *mixer)
{
    TRACE("{part} VdpVideoMixerCreate feature_count=%d, parameter_count=%d",
        feature_count, parameter_count);
#ifndef NDEBUG
    for (uint32_t k = 0; k < parameter_count; k ++) {
        printf("      parameter ");
        switch (parameters[k]) {
        case VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_WIDTH:
            printf("video surface width = %d\n", *(uint32_t*)parameter_values[k]);
            break;
        case VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_HEIGHT:
            printf("video surface height = %d\n", *(uint32_t*)parameter_values[k]);
            break;
        case VDP_VIDEO_MIXER_PARAMETER_CHROMA_TYPE:
            printf("chroma type = %s\n", reverse_chroma_type(*(uint32_t*)parameter_values[k]));
            break;
        case VDP_VIDEO_MIXER_PARAMETER_LAYERS:
            printf("layers = %d\n", *(uint32_t*)parameter_values[k]);
            break;
        default:
            return VDP_STATUS_INVALID_VIDEO_MIXER_PARAMETER;
        }
    }
#endif

    if (!handlestorage_valid(device, HANDLE_TYPE_DEVICE))
        return VDP_STATUS_INVALID_HANDLE;

    VdpVideoMixerData *data = (VdpVideoMixerData *)calloc(1, sizeof(VdpVideoMixerData));
    if (NULL == data)
        return VDP_STATUS_RESOURCES;

    data->type = HANDLE_TYPE_VIDEO_MIXER;
    data->device = device;

    *mixer = handlestorage_add(data);

    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpVideoMixerSetFeatureEnables(VdpVideoMixer mixer, uint32_t feature_count,
                                   VdpVideoMixerFeature const *features,
                                   VdpBool const *feature_enables)
{
    TRACE("{part} VdpVideoMixerSetFeatureEnables mixer=%d, feature_count=%d", mixer, feature_count);
    for (uint32_t k = 0; k < feature_count; k ++) {
        TRACE("   feature %d %s (%s)", features[k], feature_enables[k] ? "enabled" : "disabled",
            reverse_video_mixer_feature(features[k]));
    }
    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpVideoMixerSetAttributeValues(VdpVideoMixer mixer, uint32_t attribute_count,
                                    VdpVideoMixerAttribute const *attributes,
                                    void const *const *attribute_values)
{
    TRACE("{part} VdpVideoMixerSetAttributeValues mixer=%d, attribute_count=%d",
        mixer, attribute_count);
    for (uint32_t k = 0; k < attribute_count; k ++) {
        TRACE("   attribute %d (%s)", attributes[k], reverse_video_mixer_attributes(attributes[k]));
        if (VDP_VIDEO_MIXER_ATTRIBUTE_CSC_MATRIX == attributes[k]) {
            VdpCSCMatrix *matrix = (VdpCSCMatrix *)(attribute_values[k]);
            for (uint32_t j1 = 0; j1 < 3; j1 ++) {
                printf("      ");
                for (uint32_t j2 = 0; j2 < 4; j2 ++) {
                    printf("%11f", (double)((*matrix)[j1][j2]));
                }
                printf("\n");
            }
        }
    }

    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpVideoMixerGetFeatureSupport(VdpVideoMixer mixer, uint32_t feature_count,
                                   VdpVideoMixerFeature const *features, VdpBool *feature_supports)
{
    TRACE1("{zilch} VdpVideoMixerGetFeatureSupport");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpVideoMixerGetFeatureEnables(VdpVideoMixer mixer, uint32_t feature_count,
                                   VdpVideoMixerFeature const *features, VdpBool *feature_enables)
{
    TRACE1("{zilch} VdpVideoMixerGetFeatureEnables");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpVideoMixerGetParameterValues(VdpVideoMixer mixer, uint32_t parameter_count,
                                    VdpVideoMixerParameter const *parameters,
                                    void *const *parameter_values)
{
    TRACE1("{zilch} VdpVideoMixerGetParameterValues");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpVideoMixerGetAttributeValues(VdpVideoMixer mixer, uint32_t attribute_count,
                                    VdpVideoMixerAttribute const *attributes,
                                    void *const *attribute_values)
{
    TRACE1("{zilch} VdpVideoMixerGetAttributeValues");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpVideoMixerDestroy(VdpVideoMixer mixer)
{
    TRACE("{full} VdpVideoMixerDestroy mixer=%d", mixer);
    void *data = handlestorage_get(mixer, HANDLE_TYPE_VIDEO_MIXER);
    if (NULL == data)
        return VDP_STATUS_INVALID_HANDLE;

    free(data);
    handlestorage_expunge(mixer);

    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpVideoMixerRender(VdpVideoMixer mixer, VdpOutputSurface background_surface,
                        VdpRect const *background_source_rect,
                        VdpVideoMixerPictureStructure current_picture_structure,
                        uint32_t video_surface_past_count,
                        VdpVideoSurface const *video_surface_past,
                        VdpVideoSurface video_surface_current, uint32_t video_surface_future_count,
                        VdpVideoSurface const *video_surface_future,
                        VdpRect const *video_source_rect, VdpOutputSurface destination_surface,
                        VdpRect const *destination_rect, VdpRect const *destination_video_rect,
                        uint32_t layer_count, VdpLayer const *layers)
{
    TRACE1("{part} VdpVideoMixerRender");

#ifndef NDEBUG
    VdpRect const *rect;
    printf("      mixer=%d, background_surface=%d,", mixer, background_surface);
    printf(" background_source_rect=");

    rect = background_source_rect;
    if (NULL == rect) printf("NULL");
    else printf("(%d,%d,%d,%d)", rect->x0, rect->y0, rect->x1, rect->y1);

    printf(",\n      current_picture_structure=%s,\n",
        reverser_video_mixer_picture_structure(current_picture_structure));
    printf("     ");
    if (video_surface_past_count > 0) {
        printf(" video_surface_past=[");
        for (uint32_t k = 0; k < video_surface_past_count; k ++) {
            if (0 != k) printf(",");
            printf("%d", video_surface_past[k]);
        }
        printf("],");
    } else {
        printf(" no_video_surface_past,");
    }
    printf(" video_surface_current=%d,", video_surface_current);
    if (video_surface_future_count > 0) {
        printf(" video_surface_future=[");
        for (uint32_t k = 0; k < video_surface_future_count; k ++) {
            if (0 != k) printf(",");
            printf("%d", video_surface_future[k]);
        }
        printf("],");
    } else {
        printf(" no_video_surface_future,");
    }

    printf("\n      video_source_rect=");
    rect = video_source_rect;
    if (NULL == rect) printf("NULL");
    else printf("(%d,%d,%d,%d)", rect->x0, rect->y0, rect->x1, rect->y1);

    printf(", destination_surface=%d,", destination_surface);

    printf("\n      destination_rect=");
    rect = destination_rect;
    if (NULL == rect) printf("NULL");
    else printf("(%d,%d,%d,%d)", rect->x0, rect->y0, rect->x1, rect->y1);

    printf(", video_surface_current=%d,", video_surface_current);

    if (layer_count > 0) {
        printf(" layers=[");
        for (uint32_t k = 0; k < layer_count; k ++) {
            if (0 != k) printf(",");
            printf("{%d,", layers[k].source_surface);
            rect = layers[k].source_rect;
            if (NULL == rect) printf("src:NULL,");
            else printf("src:[%d,%d,%d,%d],", rect->x0, rect->y0, rect->x1, rect->y1);
            rect = layers[k].destination_rect;
            if (NULL == rect) printf("dst:NULL,");
            else printf("dst:[%d,%d,%d,%d],", rect->x0, rect->y0, rect->x1, rect->y1);
            printf("}");
        }
        printf("]");
    } else {
        printf(" no_layers");
    }

    printf("\n");
#endif

    //TODO: handle rectangles

    VdpVideoSurfaceData *source_surface =
        handlestorage_get(video_surface_current, HANDLE_TYPE_VIDEO_SURFACE);
    if (NULL == source_surface)
        return VDP_STATUS_INVALID_HANDLE;

    VdpOutputSurfaceData *dest_surface =
        handlestorage_get(destination_surface, HANDLE_TYPE_OUTPUT_SURFACE);
    if (NULL == dest_surface)
        return VDP_STATUS_INVALID_HANDLE;

    struct SwsContext *sws_ctx =
        sws_getContext(source_surface->width, source_surface->height, PIX_FMT_YUV420P,
            dest_surface->width, dest_surface->height, PIX_FMT_RGBA,
            SWS_POINT, NULL, NULL, NULL);

    uint8_t const * const src_planes[] =
        { source_surface->y_plane, source_surface->v_plane, source_surface->u_plane, NULL };
    int src_strides[] =
        {source_surface->stride, source_surface->width/2, source_surface->width/2, 0};
    uint8_t *dst_planes[] = {dest_surface->buf, NULL, NULL, NULL};
    int dst_strides[] = {dest_surface->stride * rgba_format_storage_size(dest_surface->rgba_format),
                         0, 0, 0};
    int res = sws_scale(sws_ctx,
                        src_planes, src_strides, 0, source_surface->height,
                        dst_planes, dst_strides);

    sws_freeContext(sws_ctx);

    // printf ("res = %d, while height = %d\n", res, source_surface->height);
    if (res != source_surface->height)
        return VDP_STATUS_ERROR;

    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpPresentationQueueTargetDestroy(VdpPresentationQueueTarget presentation_queue_target)
{
    TRACE("{full} VdpPresentationQueueTargetDestroy presentation_queue_target=%d",
        presentation_queue_target);
    void *data = handlestorage_get(presentation_queue_target, HANDLE_TYPE_PRESENTATION_QUEUE_TARGET);
    if (NULL == data)
        return VDP_STATUS_INVALID_HANDLE;

    free(data);
    handlestorage_expunge(presentation_queue_target);
    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpPresentationQueueCreate(VdpDevice device,
                               VdpPresentationQueueTarget presentation_queue_target,
                               VdpPresentationQueue *presentation_queue)
{
    TRACE("{part} VdpPresentationQueueCreate device=%d, presentation_queue_target=%d",
        device, presentation_queue_target);
    VdpPresentationQueueData *data =
        (VdpPresentationQueueData *)calloc(1, sizeof(VdpPresentationQueueData));
    if (NULL == data)
        return VDP_STATUS_RESOURCES;
    if (!handlestorage_valid(device, HANDLE_TYPE_DEVICE))
        return VDP_STATUS_INVALID_HANDLE;
    if (!handlestorage_valid(presentation_queue_target, HANDLE_TYPE_PRESENTATION_QUEUE_TARGET))
        return VDP_STATUS_INVALID_HANDLE;

    data->type = HANDLE_TYPE_PRESENTATION_QUEUE;
    data->device = device;
    data->presentation_queue_target = presentation_queue_target;

    *presentation_queue = handlestorage_add(data);

    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpPresentationQueueDestroy(VdpPresentationQueue presentation_queue)
{
    TRACE("{full} VdpPresentationQueueDestroy presentation_queue=%d", presentation_queue);
    void *data = handlestorage_get(presentation_queue, HANDLE_TYPE_PRESENTATION_QUEUE);
    if (NULL == data)
        return VDP_STATUS_INVALID_HANDLE;

    free(data);
    handlestorage_expunge(presentation_queue);
    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpPresentationQueueSetBackgroundColor(VdpPresentationQueue presentation_queue,
                                           VdpColor *const background_color)
{
    TRACE1("{zilch} VdpPresentationQueueSetBackgroundColor");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpPresentationQueueGetBackgroundColor(VdpPresentationQueue presentation_queue,
                                           VdpColor *background_color)
{
    TRACE1("{zilch} VdpPresentationQueueGetBackgroundColor");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpPresentationQueueGetTime(VdpPresentationQueue presentation_queue,
                                VdpTime *current_time)
{
    TRACE("{full} VdpPresentationQueueGetTime presentation_queue=%d", presentation_queue);
    struct timeval tv;
    gettimeofday(&tv, NULL);
    *current_time = (uint64_t)tv.tv_sec * 1000000000LL + (uint64_t)tv.tv_usec * 1000LL;
    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpPresentationQueueDisplay(VdpPresentationQueue presentation_queue, VdpOutputSurface surface,
                                uint32_t clip_width, uint32_t clip_height,
                                VdpTime earliest_presentation_time)
{
    TRACE("{part} VdpPresentationQueueDisplay presentation_queue=%d, surface=%d, "
        "clip_width=%d, clip_height=%d", presentation_queue, surface, clip_width, clip_height);

    VdpOutputSurfaceData *surfaceData = handlestorage_get(surface, HANDLE_TYPE_OUTPUT_SURFACE);
    if (NULL == surfaceData) return VDP_STATUS_INVALID_HANDLE;

    VdpPresentationQueueData *presentationQueueData =
        handlestorage_get(presentation_queue, HANDLE_TYPE_PRESENTATION_QUEUE);

    if (NULL == presentationQueueData) return VDP_STATUS_INVALID_HANDLE;

    VdpPresentationQueueTargetData *presentationQueueTargetData =
        handlestorage_get(presentationQueueData->presentation_queue_target,
        HANDLE_TYPE_PRESENTATION_QUEUE_TARGET);

    if (NULL == presentationQueueTargetData) return VDP_STATUS_INVALID_HANDLE;

    Drawable drawable = presentationQueueTargetData->drawable;

    VdpDeviceData *deviceData =
        handlestorage_get(presentationQueueTargetData->device, HANDLE_TYPE_DEVICE);

    if (NULL == deviceData) return VDP_STATUS_INVALID_HANDLE;

    Display *display = deviceData->display;
    int screen = deviceData->screen;

    XImage *image = XCreateImage(display, DefaultVisual(display, screen), 24, ZPixmap, 0,
        (char *)(surfaceData->buf), surfaceData->width, surfaceData->height, 32,
        surfaceData->stride * rgba_format_storage_size(surfaceData->rgba_format));

    if (NULL == image) {
        TRACE1("image become NULL in VdpPresentationQueueDisplay");
        return VDP_STATUS_RESOURCES;
    }

    XPutImage(display, drawable, DefaultGC(display, screen), image, 0, 0, 0, 0,
        surfaceData->width, surfaceData->height);

    free(image);

    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpPresentationQueueBlockUntilSurfaceIdle(VdpPresentationQueue presentation_queue,
                                              VdpOutputSurface surface,
                                              VdpTime *first_presentation_time)

{
    TRACE("{full} VdpPresentationQueueBlockUntilSurfaceIdle presentation_queue=%d, surface=%d",
        presentation_queue, surface);

    if (! handlestorage_valid(presentation_queue, HANDLE_TYPE_PRESENTATION_QUEUE))
        return VDP_STATUS_INVALID_HANDLE;

    if (! handlestorage_valid(surface, HANDLE_TYPE_OUTPUT_SURFACE))
        return VDP_STATUS_INVALID_HANDLE;

    // use current time as presentation time
    fakeVdpPresentationQueueGetTime(presentation_queue, first_presentation_time);

    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpPresentationQueueQuerySurfaceStatus(VdpPresentationQueue presentation_queue,
                                           VdpOutputSurface surface,
                                           VdpPresentationQueueStatus *status,
                                           VdpTime *first_presentation_time)
{
    *status = VDP_PRESENTATION_QUEUE_STATUS_VISIBLE;
    TRACE1("{part} VdpPresentationQueueQuerySurfaceStatus");
    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpVideoSurfaceQueryCapabilities(VdpDevice device, VdpChromaType surface_chroma_type,
                                     VdpBool *is_supported, uint32_t *max_width,
                                     uint32_t *max_height)
{
    TRACE1("{zilch} VdpVideoSurfaceQueryCapabilities");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpVideoSurfaceQueryGetPutBitsYCbCrCapabilities(VdpDevice device,
                                                    VdpChromaType surface_chroma_type,
                                                    VdpYCbCrFormat bits_ycbcr_format,
                                                    VdpBool *is_supported)
{
    TRACE1("{zilch} VdpVideoSurfaceQueryGetPutBitsYCbCrCapabilities");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpVideoSurfaceCreate(VdpDevice device, VdpChromaType chroma_type, uint32_t width,
                          uint32_t height, VdpVideoSurface *surface)
{
    TRACE("{part} VdpVideoSurfaceCreate, device=%d, chroma_type=%s, width=%d, height=%d",
        device, reverse_chroma_type(chroma_type), width, height);

    if (! handlestorage_valid(device, HANDLE_TYPE_DEVICE))
        return VDP_STATUS_INVALID_HANDLE;

    VdpVideoSurfaceData *data = (VdpVideoSurfaceData *)calloc(1, sizeof(VdpVideoSurfaceData));
    if (NULL == data)
        return VDP_STATUS_RESOURCES;

    uint32_t const stride = (width % 4 == 0) ? width : (width & ~0x3UL) + 4;

    data->type = HANDLE_TYPE_VIDEO_SURFACE;
    data->device = device;
    data->chroma_type = chroma_type;
    data->width = width;
    data->stride = stride;
    data->height = height;
    //TODO: find valid storage size for chroma_type
    data->y_plane = malloc(stride * height);
    data->v_plane = malloc(stride * height / chroma_storage_size_divider(chroma_type));
    data->u_plane = malloc(stride * height / chroma_storage_size_divider(chroma_type));
    if (NULL == data->y_plane || NULL == data->v_plane || NULL == data->u_plane) {
        if (data->y_plane) free(data->y_plane);
        if (data->v_plane) free(data->v_plane);
        if (data->u_plane) free(data->u_plane);
        free(data);
        return VDP_STATUS_RESOURCES;
    }

    *surface = handlestorage_add(data);

    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpVideoSurfaceDestroy(VdpVideoSurface surface)
{
    TRACE("{full} VdpVideoSurfaceDestroy surface=%d", surface);

    VdpVideoSurfaceData *data = handlestorage_get(surface, HANDLE_TYPE_VIDEO_SURFACE);
    if (NULL == data)
        return VDP_STATUS_INVALID_HANDLE;

    free(data->y_plane);
    free(data->v_plane);
    free(data->u_plane);
    free(data);
    handlestorage_expunge(surface);
    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpVideoSurfaceGetParameters(VdpVideoSurface surface, VdpChromaType *chroma_type,
                                 uint32_t *width, uint32_t *height)
{
    TRACE1("{zilch} VdpVideoSurfaceGetParameters");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpVideoSurfaceGetBitsYCbCr(VdpVideoSurface surface, VdpYCbCrFormat destination_ycbcr_format,
                                void *const *destination_data, uint32_t const *destination_pitches)
{
    TRACE1("{zilch} VdpVideoSurfaceGetBitsYCbCr");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpVideoSurfacePutBitsYCbCr(VdpVideoSurface surface, VdpYCbCrFormat source_ycbcr_format,
                                void const *const *source_data, uint32_t const *source_pitches)
{
    TRACE("{part} VdpVideoSurfacePutBitsYCbCr surface=%d, source_ycbcr_format=%s",
        surface, reverse_ycbcr_format(source_ycbcr_format));

    //TODO: figure out what to do with other formats
    if (VDP_YCBCR_FORMAT_YV12 != source_ycbcr_format)
        return VDP_STATUS_INVALID_Y_CB_CR_FORMAT;

    VdpVideoSurfaceData *surfaceData = handlestorage_get(surface, HANDLE_TYPE_VIDEO_SURFACE);
    if (NULL == surfaceData)
        return VDP_STATUS_INVALID_HANDLE;

    uint8_t const *src;
    uint8_t *dst;
    dst = surfaceData->y_plane;     src = source_data[0];
    for (uint32_t k = 0; k < surfaceData->height; k ++) {
        memcpy(dst, src, surfaceData->width);
        dst += surfaceData->stride;
        src += source_pitches[0];
    }

    dst = surfaceData->v_plane;     src = source_data[1];
    for (uint32_t k = 0; k < surfaceData->height / 2; k ++) {
        memcpy(dst, src, surfaceData->width / 2);
        dst += surfaceData->width / 2;
        src += source_pitches[1];
    }

    dst = surfaceData->u_plane;     src = source_data[2];
    for (uint32_t k = 0; k < surfaceData->height / 2; k ++) {
        memcpy(dst, src, surfaceData->width/2);
        dst += surfaceData->width / 2;
        src += source_pitches[2];
    }

    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpBitmapSurfaceQueryCapabilities(VdpDevice device, VdpRGBAFormat surface_rgba_format,
                                      VdpBool *is_supported, uint32_t *max_width,
                                      uint32_t *max_height)
{
    TRACE("{part} VdpBitmapSurfaceQueryCapabilities device=%d", device);
    if (! handlestorage_valid(device, HANDLE_TYPE_DEVICE))
        return VDP_STATUS_INVALID_HANDLE;

    printf("      format %s\n", reverse_rgba_format(surface_rgba_format));

    *is_supported = 1;
    *max_width = 2048;
    *max_height = 2048;

    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpBitmapSurfaceCreate(VdpDevice device, VdpRGBAFormat rgba_format, uint32_t width,
                           uint32_t height, VdpBool frequently_accessed, VdpBitmapSurface *surface)
{
    TRACE("{full} VdpBitmapSurfaceCreate device=%d, rgba_format=%s, width=%d, height=%d,"
        "frequently_accessed=%d", device, reverse_rgba_format(rgba_format), width, height,
        frequently_accessed);
    VdpDeviceData *deviceData = handlestorage_get(device, HANDLE_TYPE_DEVICE);
    if (NULL == deviceData)
        return VDP_STATUS_INVALID_HANDLE;

    VdpBitmapSurfaceData *data = (VdpBitmapSurfaceData *)calloc(1, sizeof(VdpBitmapSurfaceData));
    if (NULL == data)
        return VDP_STATUS_RESOURCES;

    uint32_t const stride = (width % 4 == 0) ? width : (width & ~0x3UL) + 4;
    void *buf = malloc(stride * height * rgba_format_storage_size(rgba_format));
    if (NULL == buf) {
        free(data);
        return VDP_STATUS_RESOURCES;
    }

    data->type = HANDLE_TYPE_BITMAP_SURFACE;
    data->device = device;
    data->rgba_format = rgba_format;
    data->width = width;
    data->height = height;
    data->stride = stride;
    data->buf = buf;

    *surface = handlestorage_add(data);
    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpBitmapSurfaceDestroy(VdpBitmapSurface surface)
{
    TRACE("{full} VdpBitmapSurfaceDestroy surface=%d", surface);
    VdpBitmapSurfaceData *data = handlestorage_get(surface, HANDLE_TYPE_BITMAP_SURFACE);
    if (NULL == data)
        return VDP_STATUS_INVALID_HANDLE;

    free(data->buf);
    free(data);
    handlestorage_expunge(surface);
    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpBitmapSurfaceGetParameters(VdpBitmapSurface surface, VdpRGBAFormat *rgba_format,
                                  uint32_t *width, uint32_t *height, VdpBool *frequently_accessed)
{
    TRACE1("{zilch} VdpBitmapSurfaceGetParameters");
    return VDP_STATUS_NO_IMPLEMENTATION;
}

static
VdpStatus
fakeVdpBitmapSurfacePutBitsNative(VdpBitmapSurface surface, void const *const *source_data,
                                  uint32_t const *source_pitches, VdpRect const *destination_rect)
{
    TRACE("{part} VdpBitmapSurfacePutBitsNative surface=%d", surface);
#ifndef NDEBUG
    printf("      destination_rect=");
    if (NULL == destination_rect) printf("NULL");
    else printf("(%d,%d,%d,%d)", destination_rect->x0, destination_rect->y0,
        destination_rect->x1, destination_rect->y1);
    printf("\n");
#endif

    VdpBitmapSurfaceData *surfaceData = handlestorage_get(surface, HANDLE_TYPE_BITMAP_SURFACE);
    if (NULL == surfaceData)
        return VDP_STATUS_INVALID_HANDLE;

    //TODO: fix handling other formats
    if (VDP_RGBA_FORMAT_B8G8R8A8 != surfaceData->rgba_format)
        return VDP_STATUS_INVALID_RGBA_FORMAT;

    VdpRect rect = {0, 0, surfaceData->width, surfaceData->height};
    if (NULL != destination_rect) rect = *destination_rect;

    for (uint32_t line = rect.y0; line < rect.y1; line ++) {
        uint8_t *dst = (uint8_t *)surfaceData->buf + (rect.x0 + line*surfaceData->stride) * 4;
        uint8_t *src = (uint8_t *)(source_data[0]) + (line - rect.y0) * source_pitches[0];
        memcpy(dst, src, 4*(rect.x1 - rect.x0));
    }

    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpDeviceDestroy(VdpDevice device)
{
    TRACE("{full} VdpDeviceDestroy device=%d", device);
    void *data = handlestorage_get(device, HANDLE_TYPE_DEVICE);
    if (NULL == data)
        return VDP_STATUS_INVALID_HANDLE;

    free(data);
    handlestorage_expunge(device);
    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpGetInformationString(char const **information_string)
{
    TRACE1("{full} VdpGetInformationString");
    *information_string = implemetation_description_string;
    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpGenerateCSCMatrix(VdpProcamp *procamp, VdpColorStandard standard, VdpCSCMatrix *csc_matrix)
{
    TRACE("{part} VdpGenerateCSCMatrix standard=%d", standard);
    if (NULL == procamp || NULL == csc_matrix)
        return VDP_STATUS_INVALID_POINTER;
    if (VDP_PROCAMP_VERSION != procamp->struct_version)
        return VDP_STATUS_INVALID_VALUE;

    VdpCSCMatrix *m = csc_matrix;
    switch (standard) {
    case VDP_COLOR_STANDARD_ITUR_BT_601:
        (*m)[0][0] = 1.16438f; (*m)[0][1] =  0.0f;     (*m)[0][2] =  1.59603f; (*m)[0][3] = -222.921f;
        (*m)[1][0] = 1.16438f; (*m)[1][1] = -0.39176f; (*m)[1][2] = -0.81297f; (*m)[1][3] =  135.576f;
        (*m)[2][0] = 1.16438f; (*m)[2][1] =  2.01723f; (*m)[2][2] =  0.0f;     (*m)[2][3] = -276.836f;
        break;
    case VDP_COLOR_STANDARD_ITUR_BT_709:
        (*m)[0][0] =  1.0f; (*m)[0][1] =  0.0f;     (*m)[0][2] =  1.402f;   (*m)[0][3] = -179.456f;
        (*m)[1][0] =  1.0f; (*m)[1][1] = -0.34414f; (*m)[1][2] = -0.71414f; (*m)[1][3] =  135.460f;
        (*m)[2][0] =  1.0f; (*m)[2][1] =  1.772f;   (*m)[2][2] =  0.0f;     (*m)[2][3] = -226.816f;
        break;
    case VDP_COLOR_STANDARD_SMPTE_240M:
        (*m)[0][0] =  0.58139f; (*m)[0][1] = -0.76437f; (*m)[0][2] =  1.5760f;  (*m)[0][3] = 0.0f;
        (*m)[1][0] =  0.58140f; (*m)[1][1] = -0.99101f; (*m)[1][2] = -0.47663f; (*m)[1][3] = 0.0f;
        (*m)[2][0] =  0.58139f; (*m)[2][1] =  1.0616f;  (*m)[2][2] =  0.00000f; (*m)[2][3] = 0.0f;
        break;
    default:
        return VDP_STATUS_INVALID_COLOR_STANDARD;
    }

    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpOutputSurfaceRenderOutputSurface(VdpOutputSurface destination_surface,
                                        VdpRect const *destination_rect,
                                        VdpOutputSurface source_surface, VdpRect const *source_rect,
                                        VdpColor const *colors,
                                        VdpOutputSurfaceRenderBlendState const *blend_state,
                                        uint32_t flags)
{
    TRACE("{dirty impl} VdpOutputSurfaceRenderOutputSurface destination_surface=%d, source_surface=%d",
        destination_surface, source_surface);
#ifndef NDEBUG
    printf("      destination_rect=");
    if (NULL == destination_rect) printf("NULL");
    else printf("(%d,%d,%d,%d)",    destination_rect->x0, destination_rect->y0,
                                    destination_rect->x1, destination_rect->y1);

    printf(", source_rect=");
    if (NULL == source_rect) printf("NULL");
    else printf("(%d,%d,%d,%d)", source_rect->x0, source_rect->y0, source_rect->x1, source_rect->y1);
    printf("\n      colors=%p, blend_state=%p, flags=%d", colors, blend_state, flags);
    printf("\n");
#endif

    //TODO: remove code duplication with fakeVdpOutputSurfaceRenderBitmapSurface
    //TODO: use swscale
    VdpOutputSurfaceData *dstSurface =
        handlestorage_get(destination_surface, HANDLE_TYPE_OUTPUT_SURFACE);
    if (NULL == dstSurface)
        return VDP_STATUS_INVALID_HANDLE;

    VdpOutputSurfaceData *srcSurface =
        handlestorage_get(source_surface, HANDLE_TYPE_OUTPUT_SURFACE);
    if (NULL == srcSurface)
        return VDP_STATUS_INVALID_HANDLE;

    memcpy(dstSurface->buf, srcSurface->buf, srcSurface->stride * srcSurface->height * 4);

    return VDP_STATUS_OK;
}

static
VdpStatus
fakeVdpOutputSurfaceRenderBitmapSurface(VdpOutputSurface destination_surface,
                                        VdpRect const *destination_rect,
                                        VdpBitmapSurface source_surface, VdpRect const *source_rect,
                                        VdpColor const *colors,
                                        VdpOutputSurfaceRenderBlendState const *blend_state,
                                        uint32_t flags)
{
    TRACE("{dirty impl} VdpOutputSurfaceRenderBitmapSurface destination_surface=%d, source_surface=%d",
        destination_surface, source_surface);
#ifndef NDEBUG
    printf("      destination_rect=");
    if (NULL == destination_rect) printf("NULL");
    else printf("(%d,%d,%d,%d)",    destination_rect->x0, destination_rect->y0,
                                    destination_rect->x1, destination_rect->y1);

    printf(", source_rect=");
    if (NULL == source_rect) printf("NULL");
    else printf("(%d,%d,%d,%d)", source_rect->x0, source_rect->y0, source_rect->x1, source_rect->y1);
    printf("\n      colors=%p, flags=%d", colors, flags);
    printf( "\n      blend_state.blend_factor_source_color=%s"
            "\n      blend_state.blend_factor_destination_color=%s"
            "\n      blend_state.blend_factor_source_alpha=%s"
            "\n      blend_state.blend_factor_destination_color=%s"
            "\n      blend_state.blend_equation_color=%s"
            "\n      blend_state.blend_equation_alpha=%s"
            "\n      blend_constant = (%11f, %11f, %11f, %11f)",
            reverse_blend_factor(blend_state->blend_factor_source_color),
            reverse_blend_factor(blend_state->blend_factor_destination_color),
            reverse_blend_factor(blend_state->blend_factor_source_alpha),
            reverse_blend_factor(blend_state->blend_factor_destination_alpha),
            reverse_blend_equation(blend_state->blend_equation_color),
            reverse_blend_equation(blend_state->blend_equation_alpha),
            blend_state->blend_constant.red, blend_state->blend_constant.green,
            blend_state->blend_constant.blue, blend_state->blend_constant.alpha
    );
    printf("\n");
#endif

    if (VDP_OUTPUT_SURFACE_RENDER_BLEND_STATE_VERSION != blend_state->struct_version)
        return VDP_STATUS_INVALID_VALUE;

    //TODO: stop doing dumb things and use swscale instead
    VdpOutputSurfaceData *dstSurface =
        handlestorage_get(destination_surface, HANDLE_TYPE_OUTPUT_SURFACE);
    if (NULL == dstSurface)
        return VDP_STATUS_INVALID_HANDLE;

    VdpBitmapSurfaceData *srcSurface =
        handlestorage_get(source_surface, HANDLE_TYPE_BITMAP_SURFACE);
    if (NULL == srcSurface)
        return VDP_STATUS_INVALID_HANDLE;

{'fraction_non_alphanumeric': 0.09480524050246895, 'fraction_numerical': 0.01545877854537351, 'mean_word_length': 3.073967889908257, 'pattern_counts': {'":': 0, '<': 45, '<?xml version=': 0, '>': 308, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 176, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2915332', 'n_tokens_mistral': 28047, 'n_tokens_neox': 26734, 'n_words': 4758}