727 lines · 26.2 KB · C
RawDownload
/* * DirectDraw Surface image decoder * Copyright (C) 2015 Vittorio Giovara <vittorio.giovara@gmail.com> * * This file is part of FFmpeg. * * FFmpeg 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. * * FFmpeg 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 FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * @file * DDS decoder * * https://msdn.microsoft.com/en-us/library/bb943982%28v=vs.85%29.aspx */ #include <stdint.h> #include "libavutil/libm.h"#include "libavutil/imgutils.h" #include "avcodec.h"#include "bytestream.h"#include "codec_internal.h"#include "decode.h"#include "texturedsp.h" #define DDPF_FOURCC    (1 <<  2)#define DDPF_PALETTE   (1 <<  5)#define DDPF_NORMALMAP (1U << 31) enum DDSPostProc {    DDS_NONE = 0,    DDS_ALPHA_EXP,    DDS_NORMAL_MAP,    DDS_RAW_YCOCG,    DDS_SWAP_ALPHA,    DDS_SWIZZLE_A2XY,    DDS_SWIZZLE_RBXG,    DDS_SWIZZLE_RGXB,    DDS_SWIZZLE_RXBG,    DDS_SWIZZLE_RXGB,    DDS_SWIZZLE_XGBR,    DDS_SWIZZLE_XRBG,    DDS_SWIZZLE_XGXR,}; enum DDSDXGIFormat {    DXGI_FORMAT_R16G16B16A16_TYPELESS       =  9,    DXGI_FORMAT_R16G16B16A16_FLOAT          = 10,    DXGI_FORMAT_R16G16B16A16_UNORM          = 11,    DXGI_FORMAT_R16G16B16A16_UINT           = 12,    DXGI_FORMAT_R16G16B16A16_SNORM          = 13,    DXGI_FORMAT_R16G16B16A16_SINT           = 14,     DXGI_FORMAT_R8G8B8A8_TYPELESS           = 27,    DXGI_FORMAT_R8G8B8A8_UNORM              = 28,    DXGI_FORMAT_R8G8B8A8_UNORM_SRGB         = 29,    DXGI_FORMAT_R8G8B8A8_UINT               = 30,    DXGI_FORMAT_R8G8B8A8_SNORM              = 31,    DXGI_FORMAT_R8G8B8A8_SINT               = 32,     DXGI_FORMAT_BC1_TYPELESS                = 70,    DXGI_FORMAT_BC1_UNORM                   = 71,    DXGI_FORMAT_BC1_UNORM_SRGB              = 72,    DXGI_FORMAT_BC2_TYPELESS                = 73,    DXGI_FORMAT_BC2_UNORM                   = 74,    DXGI_FORMAT_BC2_UNORM_SRGB              = 75,    DXGI_FORMAT_BC3_TYPELESS                = 76,    DXGI_FORMAT_BC3_UNORM                   = 77,    DXGI_FORMAT_BC3_UNORM_SRGB              = 78,    DXGI_FORMAT_BC4_TYPELESS                = 79,    DXGI_FORMAT_BC4_UNORM                   = 80,    DXGI_FORMAT_BC4_SNORM                   = 81,    DXGI_FORMAT_BC5_TYPELESS                = 82,    DXGI_FORMAT_BC5_UNORM                   = 83,    DXGI_FORMAT_BC5_SNORM                   = 84,    DXGI_FORMAT_B5G6R5_UNORM                = 85,    DXGI_FORMAT_B8G8R8A8_UNORM              = 87,    DXGI_FORMAT_B8G8R8X8_UNORM              = 88,    DXGI_FORMAT_B8G8R8A8_TYPELESS           = 90,    DXGI_FORMAT_B8G8R8A8_UNORM_SRGB         = 91,    DXGI_FORMAT_B8G8R8X8_TYPELESS           = 92,    DXGI_FORMAT_B8G8R8X8_UNORM_SRGB         = 93,}; typedef struct DDSContext {    TextureDSPContext texdsp;    GetByteContext gbc;     int compressed;    int paletted;    int bpp;    enum DDSPostProc postproc;     TextureDSPThreadContext dec;} DDSContext; static int parse_pixel_format(AVCodecContext *avctx){    DDSContext *ctx = avctx->priv_data;    GetByteContext *gbc = &ctx->gbc;    uint32_t flags, fourcc, gimp_tag;    enum DDSDXGIFormat dxgi;    int size, bpp, r, g, b, a;    int alpha_exponent, ycocg_classic, ycocg_scaled, normal_map, array;     /* Alternative DDS implementations use reserved1 as custom header. */    bytestream2_skip(gbc, 4 * 3);    gimp_tag = bytestream2_get_le32(gbc);    alpha_exponent = gimp_tag == MKTAG('A', 'E', 'X', 'P');    ycocg_classic  = gimp_tag == MKTAG('Y', 'C', 'G', '1');    ycocg_scaled   = gimp_tag == MKTAG('Y', 'C', 'G', '2');    bytestream2_skip(gbc, 4 * 7);     /* Now the real DDPF starts. */    size = bytestream2_get_le32(gbc);    if (size != 32) {        av_log(avctx, AV_LOG_ERROR, "Invalid pixel format header %d.\n", size);        return AVERROR_INVALIDDATA;    }    flags = bytestream2_get_le32(gbc);    ctx->compressed = flags & DDPF_FOURCC;    ctx->paletted   = flags & DDPF_PALETTE;    normal_map      = flags & DDPF_NORMALMAP;    fourcc = bytestream2_get_le32(gbc);     if (ctx->compressed && ctx->paletted) {        av_log(avctx, AV_LOG_WARNING,               "Disabling invalid palette flag for compressed dds.\n");        ctx->paletted = 0;    }     bpp = ctx->bpp = bytestream2_get_le32(gbc); // rgbbitcount    r   = bytestream2_get_le32(gbc); // rbitmask    g   = bytestream2_get_le32(gbc); // gbitmask    b   = bytestream2_get_le32(gbc); // bbitmask    a   = bytestream2_get_le32(gbc); // abitmask     bytestream2_skip(gbc, 4); // caps    bytestream2_skip(gbc, 4); // caps2    bytestream2_skip(gbc, 4); // caps3    bytestream2_skip(gbc, 4); // caps4    bytestream2_skip(gbc, 4); // reserved2     av_log(avctx, AV_LOG_VERBOSE, "fourcc %s bpp %d "           "r 0x%x g 0x%x b 0x%x a 0x%x\n", av_fourcc2str(fourcc), bpp, r, g, b, a);    if (gimp_tag)        av_log(avctx, AV_LOG_VERBOSE, "and GIMP-DDS tag %s\n", av_fourcc2str(gimp_tag));     if (ctx->compressed)        avctx->pix_fmt = AV_PIX_FMT_RGBA;     if (ctx->compressed) {        ctx->dec.raw_ratio = 16;        switch (fourcc) {        case MKTAG('D', 'X', 'T', '1'):            ctx->dec.tex_ratio = 8;            ctx->dec.tex_funct = ctx->texdsp.dxt1a_block;            break;        case MKTAG('D', 'X', 'T', '2'):            ctx->dec.tex_ratio = 16;            ctx->dec.tex_funct = ctx->texdsp.dxt2_block;            break;        case MKTAG('D', 'X', 'T', '3'):            ctx->dec.tex_ratio = 16;            ctx->dec.tex_funct = ctx->texdsp.dxt3_block;            break;        case MKTAG('D', 'X', 'T', '4'):            ctx->dec.tex_ratio = 16;            ctx->dec.tex_funct = ctx->texdsp.dxt4_block;            break;        case MKTAG('D', 'X', 'T', '5'):            ctx->dec.tex_ratio = 16;            if (ycocg_scaled)                ctx->dec.tex_funct = ctx->texdsp.dxt5ys_block;            else if (ycocg_classic)                ctx->dec.tex_funct = ctx->texdsp.dxt5y_block;            else                ctx->dec.tex_funct = ctx->texdsp.dxt5_block;            break;        case MKTAG('R', 'X', 'G', 'B'):            ctx->dec.tex_ratio = 16;            ctx->dec.tex_funct = ctx->texdsp.dxt5_block;            /* This format may be considered as a normal map,             * but it is handled differently in a separate postproc. */            ctx->postproc = DDS_SWIZZLE_RXGB;            normal_map = 0;            break;        case MKTAG('A', 'T', 'I', '1'):        case MKTAG('B', 'C', '4', 'U'):            ctx->dec.tex_ratio = 8;            ctx->dec.tex_funct = ctx->texdsp.rgtc1u_block;            break;        case MKTAG('B', 'C', '4', 'S'):            ctx->dec.tex_ratio = 8;            ctx->dec.tex_funct = ctx->texdsp.rgtc1s_block;            break;        case MKTAG('A', 'T', 'I', '2'):            /* RGT2 variant with swapped R and G (3Dc)*/            ctx->dec.tex_ratio = 16;            ctx->dec.tex_funct = ctx->texdsp.dxn3dc_block;            break;        case MKTAG('B', 'C', '5', 'U'):            ctx->dec.tex_ratio = 16;            ctx->dec.tex_funct = ctx->texdsp.rgtc2u_block;            break;        case MKTAG('B', 'C', '5', 'S'):            ctx->dec.tex_ratio = 16;            ctx->dec.tex_funct = ctx->texdsp.rgtc2s_block;            break;        case MKTAG('U', 'Y', 'V', 'Y'):            ctx->compressed = 0;            avctx->pix_fmt = AV_PIX_FMT_UYVY422;            break;        case MKTAG('Y', 'U', 'Y', '2'):            ctx->compressed = 0;            avctx->pix_fmt = AV_PIX_FMT_YUYV422;            break;        case MKTAG('P', '8', ' ', ' '):            /* ATI Palette8, same as normal palette */            ctx->compressed = 0;            ctx->paletted   = 1;            avctx->pix_fmt  = AV_PIX_FMT_PAL8;            break;        case MKTAG('G', '1', ' ', ' '):            ctx->compressed = 0;            avctx->pix_fmt  = AV_PIX_FMT_MONOBLACK;            break;        case MKTAG('D', 'X', '1', '0'):            /* DirectX 10 extra header */            dxgi = bytestream2_get_le32(gbc);            bytestream2_skip(gbc, 4); // resourceDimension            bytestream2_skip(gbc, 4); // miscFlag            array = bytestream2_get_le32(gbc);            bytestream2_skip(gbc, 4); // miscFlag2             if (array != 0)                av_log(avctx, AV_LOG_VERBOSE,                       "Found array of size %d (ignored).\n", array);             /* Only BC[1-5] are actually compressed. */            ctx->compressed = (dxgi >= 70) && (dxgi <= 84);             av_log(avctx, AV_LOG_VERBOSE, "DXGI format %d.\n", dxgi);            switch (dxgi) {            /* RGB types. */            case DXGI_FORMAT_R16G16B16A16_TYPELESS:            case DXGI_FORMAT_R16G16B16A16_FLOAT:            case DXGI_FORMAT_R16G16B16A16_UNORM:            case DXGI_FORMAT_R16G16B16A16_UINT:            case DXGI_FORMAT_R16G16B16A16_SNORM:            case DXGI_FORMAT_R16G16B16A16_SINT:                avctx->pix_fmt = AV_PIX_FMT_BGRA64;                break;            case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:                avctx->colorspace = AVCOL_SPC_RGB;            case DXGI_FORMAT_R8G8B8A8_TYPELESS:            case DXGI_FORMAT_R8G8B8A8_UNORM:            case DXGI_FORMAT_R8G8B8A8_UINT:            case DXGI_FORMAT_R8G8B8A8_SNORM:            case DXGI_FORMAT_R8G8B8A8_SINT:                avctx->pix_fmt = AV_PIX_FMT_BGRA;                break;            case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:                avctx->colorspace = AVCOL_SPC_RGB;            case DXGI_FORMAT_B8G8R8A8_TYPELESS:            case DXGI_FORMAT_B8G8R8A8_UNORM:                avctx->pix_fmt = AV_PIX_FMT_RGBA;                break;            case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB:                avctx->colorspace = AVCOL_SPC_RGB;            case DXGI_FORMAT_B8G8R8X8_TYPELESS:            case DXGI_FORMAT_B8G8R8X8_UNORM:                avctx->pix_fmt = AV_PIX_FMT_RGBA; // opaque                break;            case DXGI_FORMAT_B5G6R5_UNORM:                avctx->pix_fmt = AV_PIX_FMT_RGB565LE;                break;            /* Texture types. */            case DXGI_FORMAT_BC1_UNORM_SRGB:                avctx->colorspace = AVCOL_SPC_RGB;            case DXGI_FORMAT_BC1_TYPELESS:            case DXGI_FORMAT_BC1_UNORM:                ctx->dec.tex_ratio = 8;                ctx->dec.tex_funct = ctx->texdsp.dxt1a_block;                break;            case DXGI_FORMAT_BC2_UNORM_SRGB:                avctx->colorspace = AVCOL_SPC_RGB;            case DXGI_FORMAT_BC2_TYPELESS:            case DXGI_FORMAT_BC2_UNORM:                ctx->dec.tex_ratio = 16;                ctx->dec.tex_funct = ctx->texdsp.dxt3_block;                break;            case DXGI_FORMAT_BC3_UNORM_SRGB:                avctx->colorspace = AVCOL_SPC_RGB;            case DXGI_FORMAT_BC3_TYPELESS:            case DXGI_FORMAT_BC3_UNORM:                ctx->dec.tex_ratio = 16;                ctx->dec.tex_funct = ctx->texdsp.dxt5_block;                break;            case DXGI_FORMAT_BC4_TYPELESS:            case DXGI_FORMAT_BC4_UNORM:                ctx->dec.tex_ratio = 8;                ctx->dec.tex_funct = ctx->texdsp.rgtc1u_block;                break;            case DXGI_FORMAT_BC4_SNORM:                ctx->dec.tex_ratio = 8;                ctx->dec.tex_funct = ctx->texdsp.rgtc1s_block;                break;            case DXGI_FORMAT_BC5_TYPELESS:            case DXGI_FORMAT_BC5_UNORM:                ctx->dec.tex_ratio = 16;                ctx->dec.tex_funct = ctx->texdsp.rgtc2u_block;                break;            case DXGI_FORMAT_BC5_SNORM:                ctx->dec.tex_ratio = 16;                ctx->dec.tex_funct = ctx->texdsp.rgtc2s_block;                break;            default:                av_log(avctx, AV_LOG_ERROR,                       "Unsupported DXGI format %d.\n", dxgi);                return AVERROR_INVALIDDATA;            }            break;        default:            av_log(avctx, AV_LOG_ERROR, "Unsupported %s fourcc.\n", av_fourcc2str(fourcc));            return AVERROR_INVALIDDATA;        }    } else if (ctx->paletted) {        if (bpp == 8) {            avctx->pix_fmt = AV_PIX_FMT_PAL8;        } else {            av_log(avctx, AV_LOG_ERROR, "Unsupported palette bpp %d.\n", bpp);            return AVERROR_INVALIDDATA;        }    } else {        /*  4 bpp */        if (bpp == 4 && r == 0 && g == 0 && b == 0 && a == 0)            avctx->pix_fmt = AV_PIX_FMT_PAL8;        /*  8 bpp */        else if (bpp == 8 && r == 0xff && g == 0 && b == 0 && a == 0)            avctx->pix_fmt = AV_PIX_FMT_GRAY8;        else if (bpp == 8 && r == 0 && g == 0 && b == 0 && a == 0xff)            avctx->pix_fmt = AV_PIX_FMT_GRAY8;        /* 16 bpp */        else if (bpp == 16 && r == 0xff && g == 0 && b == 0 && a == 0xff00)            avctx->pix_fmt = AV_PIX_FMT_YA8;        else if (bpp == 16 && r == 0xff00 && g == 0 && b == 0 && a == 0xff) {            avctx->pix_fmt = AV_PIX_FMT_YA8;            ctx->postproc = DDS_SWAP_ALPHA;        }        else if (bpp == 16 && r == 0xffff && g == 0 && b == 0 && a == 0)            avctx->pix_fmt = AV_PIX_FMT_GRAY16LE;        else if (bpp == 16 && r == 0x7c00 && g == 0x3e0 && b == 0x1f && a == 0)            avctx->pix_fmt = AV_PIX_FMT_RGB555LE;        else if (bpp == 16 && r == 0x7c00 && g == 0x3e0 && b == 0x1f && a == 0x8000)            avctx->pix_fmt = AV_PIX_FMT_RGB555LE; // alpha ignored        else if (bpp == 16 && r == 0xf800 && g == 0x7e0 && b == 0x1f && a == 0)            avctx->pix_fmt = AV_PIX_FMT_RGB565LE;        /* 24 bpp */        else if (bpp == 24 && r == 0xff0000 && g == 0xff00 && b == 0xff && a == 0)            avctx->pix_fmt = AV_PIX_FMT_BGR24;        /* 32 bpp */        else if (bpp == 32 && r == 0xff0000 && g == 0xff00 && b == 0xff && a == 0)            avctx->pix_fmt = AV_PIX_FMT_BGR0; // opaque        else if (bpp == 32 && r == 0xff && g == 0xff00 && b == 0xff0000 && a == 0)            avctx->pix_fmt = AV_PIX_FMT_RGB0; // opaque        else if (bpp == 32 && r == 0xff0000 && g == 0xff00 && b == 0xff && a == 0xff000000)            avctx->pix_fmt = AV_PIX_FMT_BGRA;        else if (bpp == 32 && r == 0xff && g == 0xff00 && b == 0xff0000 && a == 0xff000000)            avctx->pix_fmt = AV_PIX_FMT_RGBA;        /* give up */        else {            av_log(avctx, AV_LOG_ERROR, "Unknown pixel format "                   "[bpp %d r 0x%x g 0x%x b 0x%x a 0x%x].\n", bpp, r, g, b, a);            return AVERROR_INVALIDDATA;        }    }     /* Set any remaining post-proc that should happen before frame is ready. */    if (alpha_exponent)        ctx->postproc = DDS_ALPHA_EXP;    else if (normal_map)        ctx->postproc = DDS_NORMAL_MAP;    else if (ycocg_classic && !ctx->compressed)        ctx->postproc = DDS_RAW_YCOCG;     /* ATI/NVidia variants sometimes add swizzling in bpp. */    switch (bpp) {    case MKTAG('A', '2', 'X', 'Y'):        ctx->postproc = DDS_SWIZZLE_A2XY;        break;    case MKTAG('x', 'G', 'B', 'R'):        ctx->postproc = DDS_SWIZZLE_XGBR;        break;    case MKTAG('x', 'R', 'B', 'G'):        ctx->postproc = DDS_SWIZZLE_XRBG;        break;    case MKTAG('R', 'B', 'x', 'G'):        ctx->postproc = DDS_SWIZZLE_RBXG;        break;    case MKTAG('R', 'G', 'x', 'B'):        ctx->postproc = DDS_SWIZZLE_RGXB;        break;    case MKTAG('R', 'x', 'B', 'G'):        ctx->postproc = DDS_SWIZZLE_RXBG;        break;    case MKTAG('x', 'G', 'x', 'R'):        ctx->postproc = DDS_SWIZZLE_XGXR;        break;    case MKTAG('A', '2', 'D', '5'):        ctx->postproc = DDS_NORMAL_MAP;        break;    }     return 0;} static void do_swizzle(AVFrame *frame, int x, int y){    int i;    for (i = 0; i < frame->linesize[0] * frame->height; i += 4) {        uint8_t *src = frame->data[0] + i;        FFSWAP(uint8_t, src[x], src[y]);    }} static void run_postproc(AVCodecContext *avctx, AVFrame *frame){    DDSContext *ctx = avctx->priv_data;    int i, x_off;     switch (ctx->postproc) {    case DDS_ALPHA_EXP:        /* Alpha-exponential mode divides each channel by the maximum         * R, G or B value, and stores the multiplying factor in the         * alpha channel. */        av_log(avctx, AV_LOG_DEBUG, "Post-processing alpha exponent.\n");         for (i = 0; i < frame->linesize[0] * frame->height; i += 4) {            uint8_t *src = frame->data[0] + i;            int r = src[0];            int g = src[1];            int b = src[2];            int a = src[3];             src[0] = r * a / 255;            src[1] = g * a / 255;            src[2] = b * a / 255;            src[3] = 255;        }        break;    case DDS_NORMAL_MAP:        /* Normal maps work in the XYZ color space and they encode         * X in R or in A, depending on the texture type, Y in G and         * derive Z with a square root of the distance.         *         * http://www.realtimecollisiondetection.net/blog/?p=28 */        av_log(avctx, AV_LOG_DEBUG, "Post-processing normal map.\n");         x_off = ctx->dec.tex_ratio == 8 ? 0 : 3;        for (i = 0; i < frame->linesize[0] * frame->height; i += 4) {            uint8_t *src = frame->data[0] + i;            int x = src[x_off];            int y = src[1];            int z = 127;             int d = (255 * 255 - x * x - y * y) / 2;            if (d > 0)                z = lrint(sqrtf(d));             src[0] = x;            src[1] = y;            src[2] = z;            src[3] = 255;        }        break;    case DDS_RAW_YCOCG:        /* Data is Y-Co-Cg-A and not RGBA, but they are represented         * with the same masks in the DDPF header. */        av_log(avctx, AV_LOG_DEBUG, "Post-processing raw YCoCg.\n");         for (i = 0; i < frame->linesize[0] * frame->height; i += 4) {            uint8_t *src = frame->data[0] + i;            int a  = src[0];            int cg = src[1] - 128;            int co = src[2] - 128;

Lines 1–500 of 727.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t