Dreamcast Changes

This commit is contained in:
The Gang 2024-12-26 11:10:17 +02:00 committed by Stefanos Kornilios Mitsis Poiitidis
parent 310dd86371
commit 29550ea045
555 changed files with 206934 additions and 1507 deletions

29
.gitignore vendored
View File

@ -360,4 +360,31 @@ codewarrior/re3_Data/
codewarrior/Release/
codewarrior/Debug/
src/extras/GitSHA1.cpp
src/extras/GitSHA1.cpp
*.o
*.d
*.sim.o
*.sim.o3
dreamcast/1ST_READ.BIN
dreamcast/IP.BIN
dreamcast/re3.cdi
dreamcast/re3.iso
dreamcast/re3.elf
dreamcast/re3.elf.bin
dreamcast/re3-sim.elf
dreamcast/dca3.cdi
dreamcast/dca3.iso
dreamcast/dca3.elf
dreamcast/dca3.elf.bin
dreamcast/dca3-sim.elf
dreamcast/texconv*
dreamcast/imgtool*
dreamcast/extract-sfx*
dreamcast/pack-sfx*
dreamcast/analyze-profile*
dreamcast/aud2adpcm*
dreamcast/repack-data
dreamcast/output.map
dreamcast/dca3.ds.iso
.DS_Store

151
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,151 @@
variables:
GIT_SUBMODULE_STRATEGY: recursive
image:
name: ghcr.io/kos-builds/kos-dc:latest-14.1.0
entrypoint: ["/bin/sh", "-c", 'echo gitlab command: "$@" && /bin/bash -c "$@"']
build-dreamcast:
variables:
CCACHE_BASEDIR: $CI_PROJECT_DIR
CCACHE_DIR: $CI_PROJECT_DIR/ccache
cache:
- key: ccache-$CI_JOB_NAME
paths:
- $CCACHE_DIR
stage: build
before_script:
- apt update
- apt install -y ccache
- update-ccache-symlinks
- rm -rf /opt/toolchains/dc/kos
- cp -R vendor/dca3-kos /opt/toolchains/dc/kos
- make -C /opt/toolchains/dc/kos/kernel/arch/dreamcast/sound/arm
- make -C /opt/toolchains/dc/kos -j $(nproc)
script:
- cd dreamcast
- make -j $(nproc)
artifacts:
paths:
- dreamcast/dca3.elf
build-dreamcast-ide-32mb:
variables:
CCACHE_BASEDIR: $CI_PROJECT_DIR
CCACHE_DIR: $CI_PROJECT_DIR/ccache
cache:
- key: ccache-$CI_JOB_NAME
paths:
- $CCACHE_DIR
stage: build
before_script:
- apt update
- apt install -y ccache
- update-ccache-symlinks
- rm -rf /opt/toolchains/dc/kos
- cp -R vendor/dca3-kos /opt/toolchains/dc/kos
- make -C /opt/toolchains/dc/kos/kernel/arch/dreamcast/sound/arm
- make -C /opt/toolchains/dc/kos -j $(nproc)
script:
- cd dreamcast
- make -j $(nproc) WITH_IDE=1 WITH_32MB=1 TARGET=dca3-ide-32mb.elf
artifacts:
paths:
- dreamcast/dca3-ide-32mb.elf
build-texconv:
variables:
CCACHE_BASEDIR: $CI_PROJECT_DIR
CCACHE_DIR: $CI_PROJECT_DIR/ccache
cache:
- key: ccache-$CI_JOB_NAME
paths:
- $CCACHE_DIR
stage: build
before_script:
- apt update
- apt install -y build-essential gcc g++ ccache
- update-ccache-symlinks
script:
- export PATH="/usr/lib/ccache:$PATH"
- cd dreamcast
- make texconv -j $(nproc)
artifacts:
paths:
- dreamcast/texconv
build-texconv-clang:
variables:
CCACHE_BASEDIR: $CI_PROJECT_DIR
CCACHE_DIR: $CI_PROJECT_DIR/ccache
cache:
- key: ccache-$CI_JOB_NAME
paths:
- $CCACHE_DIR
stage: build
before_script:
- apt update
- apt install -y build-essential gcc g++ clang ccache
- update-ccache-symlinks
script:
- export PATH="/usr/lib/ccache:$PATH"
- cd dreamcast
- CC=clang CXX=clang++ make texconv -j $(nproc)
- mv texconv texconv-clang
artifacts:
paths:
- dreamcast/texconv-clang
build-sim:
variables:
CCACHE_BASEDIR: $CI_PROJECT_DIR
CCACHE_DIR: $CI_PROJECT_DIR/ccache
cache:
- key: ccache-$CI_JOB_NAME
paths:
- $CCACHE_DIR
stage: build
before_script:
- dpkg --add-architecture i386
- apt update
- apt install -y build-essential gcc g++ gcc-multilib g++-multilib libx11-dev:i386 ccache
- update-ccache-symlinks
script:
- export PATH="/usr/lib/ccache:$PATH"
- cd dreamcast
- make -f sim.mk -j $(nproc)
artifacts:
paths:
- dreamcast/dca3-sim.elf
build-sim-clang:
variables:
CCACHE_BASEDIR: $CI_PROJECT_DIR
CCACHE_DIR: $CI_PROJECT_DIR/ccache
cache:
- key: ccache-$CI_JOB_NAME
paths:
- $CCACHE_DIR
stage: build
before_script:
- dpkg --add-architecture i386
- apt update
- apt install -y build-essential gcc g++ gcc-multilib g++-multilib libx11-dev:i386 clang ccache
- update-ccache-symlinks
script:
- export PATH="/usr/lib/ccache:$PATH"
- cd dreamcast
- CC=clang CXX=clang++ make -f sim.mk -j $(nproc) TARGET=dca3-sim-clang.elf
artifacts:
paths:
- dreamcast/dca3-sim-clang.elf
pages:
stage: deploy
script:
- echo "The site will be deployed to $CI_PAGES_URL"
artifacts:
paths:
- public
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

7
.gitmodules vendored
View File

@ -10,7 +10,6 @@
path = vendor/opusfile
url = https://github.com/xiph/opusfile.git
branch = master
[submodule "vendor/librw"]
path = vendor/librw
url = https://github.com/aap/librw.git
branch = master
[submodule "vendor/dca3-kos"]
path = vendor/dca3-kos
url = https://gitlab.com/skmp/dca3-kos.git

View File

@ -1,50 +1,60 @@
{
"configurations": [
{
"name": "Mac",
"includePath": ["${default}"],
"defines": [],
"macFrameworkPath": [
"/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks"
],
"compilerPath": "/opt/local/bin/clang",
"compilerArgs": ["-g"],
"cStandard": "gnu11",
"cppStandard": "gnu++14",
"browse": {
"path": [
"/opt/local/include",
"/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include"
]
}
},
{
"name": "Linux",
"includePath": ["${default}"],
"defines": ["XDG_ROOT"],
"compilerPath": "/usr/bin/gcc",
"compilerArgs": ["-ggdb"],
"cStandard": "gnu11",
"cppStandard": "gnu++14"
},
{
"name": "devkitPro aarch64 (Nintendo Switch)",
"compilerPath": "${env:DEVKITPRO}/devkitA64/bin/aarch64-none-elf-g++",
"includePath": [
"${default}",
"${env:DEVKITPRO}/portlibs/switch/include",
"${env:DEVKITPRO}/libnx/include"
],
"intelliSenseMode": "gcc-arm64",
"cStandard": "gnu11",
"cppStandard": "gnu++11",
"defines": [
"__SWITCH__",
"LIBRW",
"RW_GL3",
"AUDIO_OAL"
]
}
],
"version": 4
}
"configurations": [
{
"name": "Mac",
"includePath": [
"${default}"
],
"defines": ["LIBRW", "RW_DC"],
"macFrameworkPath": [
"/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks"
],
"compilerPath": "/opt/local/bin/clang",
"compilerArgs": [
"-g"
],
"cStandard": "gnu11",
"cppStandard": "gnu++14",
"browse": {
"path": [
"/opt/local/include",
"/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include"
]
}
},
{
"name": "Linux",
"includePath": [
"${default}"
],
"defines": [
"XDG_ROOT",
"LIBRW",
"RW_DC"
],
"compilerPath": "/usr/bin/gcc",
"compilerArgs": [
"-ggdb"
],
"cStandard": "gnu11",
"cppStandard": "gnu++14"
},
{
"name": "devkitPro aarch64 (Nintendo Switch)",
"compilerPath": "${env:DEVKITPRO}/devkitA64/bin/aarch64-none-elf-g++",
"includePath": [
"${default}",
"${env:DEVKITPRO}/portlibs/switch/include",
"${env:DEVKITPRO}/libnx/include"
],
"intelliSenseMode": "gcc-arm64",
"cStandard": "gnu11",
"cppStandard": "gnu++11",
"defines": [
"LIBRW",
"RW_DC"
]
}
],
"version": 4
}

108
.vscode/launch.json vendored
View File

@ -1,5 +1,113 @@
{
"configurations": [
{
"name": "dca3-sim",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/dreamcast/dca3-sim.elf",
"args": [],
"stopAtEntry": false,
"cwd": "${fileDirname}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Set Disassembly Flavor to Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
},
{
"name": "dca3-sim (mac)",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/dreamcast/dca3-sim.elf",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/dreamcast",
"environment": [],
"externalConsole": false,
"MIMode": "lldb"
},
{
"name": "texconv (Windows)",
"type": "cppvsdbg",
"request": "launch",
"program": "${workspaceFolder}/dreamcast/texconv.exe",
"args": ["repack-data/img-orig/GTAElift.DFF", "repack-data/img-dc/GTAElift.DFF"],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/dreamcast",
"environment": [],
"console": "externalTerminal"
},
{
"name": "wav2adpcm (wav, mac)",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/dreamcast/wav2adpcm",
"args": ["-t", "../../gta3/audio/CLASS.wav", "repack-data/gta3/stream/CLASS.wav"],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/dreamcast",
"environment": [],
"externalConsole": false,
"MIMode": "lldb"
},
{
"name": "wav2adpcm (mp3, mac)",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/dreamcast/wav2adpcm",
"args": ["-t", "../../gta3/audio/JB.mp3", "repack-data/gta3/stream/JB.wav"],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/dreamcast",
"environment": [],
"externalConsole": false,
"MIMode": "lldb"
},
{
"name": "texconv (mac)",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/dreamcast/texconv",
"args": ["repack-data/img-orig/IslandLODcomSUB.dff", "repack-data/img-dc/IslandLODcomSUB.dff"],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/dreamcast",
"environment": [],
"externalConsole": false,
"MIMode": "lldb"
},
{
"name": "texconv",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/dreamcast/texconv",
"args": ["../../gta3/models/fonts.txd", "repack/gta3/models/fonts.txd", "32", "32"],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/dreamcast",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Set Disassembly Flavor to Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
},
{
"MIMode": "gdb",
"args": [],

111
.vscode/settings.json vendored
View File

@ -32,5 +32,114 @@
"C_Cpp.vcFormat.space.pointerReferenceAlignment": "right",
"cSpell.enabled": false,
"files.trimFinalNewlines": false,
"files.trimTrailingWhitespace": false
"files.trimTrailingWhitespace": false,
"files.associations": {
"system_error": "cpp",
"xlocale": "cpp",
"xiosbase": "cpp",
"memory": "cpp",
"mutex": "cpp",
"xmemory": "cpp",
"deque": "cpp",
"initializer_list": "cpp",
"list": "cpp",
"queue": "cpp",
"type_traits": "cpp",
"vector": "cpp",
"xhash": "cpp",
"xstring": "cpp",
"xutility": "cpp",
"iosfwd": "cpp",
"algorithm": "cpp",
"atomic": "cpp",
"iterator": "cpp",
"xtree": "cpp",
"ios": "cpp",
"*.inc": "cpp",
"cstdlib": "cpp",
"ratio": "cpp",
"array": "cpp",
"functional": "cpp",
"tuple": "cpp",
"utility": "cpp",
"*.tcc": "cpp",
"unordered_map": "cpp",
"chrono": "cpp",
"bit": "cpp",
"cctype": "cpp",
"cinttypes": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"compare": "cpp",
"concepts": "cpp",
"condition_variable": "cpp",
"csignal": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"map": "cpp",
"set": "cpp",
"string": "cpp",
"exception": "cpp",
"memory_resource": "cpp",
"numeric": "cpp",
"random": "cpp",
"string_view": "cpp",
"fstream": "cpp",
"iostream": "cpp",
"istream": "cpp",
"limits": "cpp",
"new": "cpp",
"numbers": "cpp",
"ostream": "cpp",
"semaphore": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"stop_token": "cpp",
"streambuf": "cpp",
"thread": "cpp",
"typeinfo": "cpp",
"codecvt": "cpp",
"iomanip": "cpp",
"format": "cpp",
"__locale": "cpp",
"optional": "cpp",
"span": "cpp",
"__bit_reference": "cpp",
"__config": "cpp",
"__hash_table": "cpp",
"__node_handle": "cpp",
"__split_buffer": "cpp",
"__threading_support": "cpp",
"__tree": "cpp",
"__verbose_abort": "cpp",
"bitset": "cpp",
"charconv": "cpp",
"complex": "cpp",
"execution": "cpp",
"locale": "cpp",
"variant": "cpp",
"text_encoding": "cpp",
"stack": "cpp",
"unordered_set": "cpp",
"shared_mutex": "cpp",
"forward_list": "cpp",
"cholesky": "cpp",
"core": "cpp",
"eigenvalues": "cpp",
"geometry": "cpp",
"householder": "cpp",
"jacobi": "cpp",
"lu": "cpp",
"qr": "cpp",
"svd": "cpp",
"print": "cpp",
"strstream": "cpp",
"regex": "cpp"
}
}

203
README.md
View File

@ -1,206 +1,23 @@
<img src="https://github.com/halpz/re3/blob/master/res/images/logo_1024.png?raw=true" alt="re3 logo" width="200">
[![Build Status](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2FGTAmodding%2Fre3%2Fbadge%3Fref%3Dmaster&style=flat)](https://actions-badge.atrox.dev/GTAmodding/re3/goto?ref=master)
<a href="https://discord.gg/RFNbjsUMGg"><img src="https://img.shields.io/badge/discord-join-7289DA.svg?logo=discord&longCache=true&style=flat" /></a>
## Intro
In this repository you'll find the fully reversed source code for GTA III ([master](https://github.com/halpz/re3/tree/master/) branch) and GTA VC ([miami](https://github.com/halpz/re3/tree/miami/) branch).
dca3 is a port of GTA III for the Dreamcast made by The Gang, using [re3](https://github.com/halpz/re3/tree/master/) as a base.
It has been tested and works on Windows, Linux, MacOS and FreeBSD, on x86, amd64, arm and arm64.\
Rendering is handled either by original RenderWare (D3D8)
or the reimplementation [librw](https://github.com/aap/librw) (D3D9, OpenGL 2.1 or above, OpenGL ES 2.0 or above).\
Audio is done with MSS (using dlls from original GTA) or OpenAL.
re3 a fully reversed source code for GTA III.
The project has also been ported to the [Nintendo Switch](https://github.com/AGraber/re3-nx/),
[Playstation Vita](https://github.com/Rinnegatamante/re3) and
[Nintendo Wii U](https://github.com/GaryOderNichts/re3-wiiu/).
This project was started by [Stefanos Kornilios Mitsis Poiitidis](https://x.com/poiitidis) and uses [KallistiOS](https://kos-docs.dreamcast.wiki/).
We cannot build for PS2 or Xbox yet. If you're interested in doing so, get in touch with us.
## Building
## Installation
dca3 requires PC game assets to work, so you **must** have GTA III. We have tested the 2cd version, others might work.
- re3 requires PC game assets to work, so you **must** own [a copy of GTA III](https://store.steampowered.com/app/12100/Grand_Theft_Auto_III/).
- Build re3 or download the latest build:
- [Windows D3D9 MSS 32bit](https://nightly.link/GTAmodding/re3/workflows/re3_msvc_x86/master/re3_Release_win-x86-librw_d3d9-mss.zip)
- [Windows D3D9 64bit](https://nightly.link/GTAmodding/re3/workflows/re3_msvc_amd64/master/re3_Release_win-amd64-librw_d3d9-oal.zip)
- [Windows OpenGL 64bit](https://nightly.link/GTAmodding/re3/workflows/re3_msvc_amd64/master/re3_Release_win-amd64-librw_gl3_glfw-oal.zip)
- [Linux 64bit](https://nightly.link/GTAmodding/re3/workflows/build-cmake-conan/master/ubuntu-18.04-gl3.zip)
- [MacOS 64bit x86-64](https://nightly.link/GTAmodding/re3/workflows/build-cmake-conan/master/macos-latest-gl3.zip)
- Extract the downloaded zip over your GTA 3 directory and run re3. The zip includes the binary, updated and additional gamefiles and in case of OpenAL the required dlls.
## Screenshots
![re3 2021-02-11 22-57-03-23](https://user-images.githubusercontent.com/1521437/107704085-fbdabd00-6cbc-11eb-8406-8951a80ccb16.png)
![re3 2021-02-11 22-43-44-98](https://user-images.githubusercontent.com/1521437/107703339-cbdeea00-6cbb-11eb-8f0b-07daa105d470.png)
![re3 2021-02-11 22-46-33-76](https://user-images.githubusercontent.com/1521437/107703343-cd101700-6cbb-11eb-9ccd-012cb90524b7.png)
![re3 2021-02-11 22-50-29-54](https://user-images.githubusercontent.com/1521437/107703348-d00b0780-6cbb-11eb-8afd-054249c2b95e.png)
## Improvements
We have implemented a number of changes and improvements to the original game.
They can be configured in `core/config.h`.
Some of them can be toggled at runtime, some cannot.
* Fixed a lot of smaller and bigger bugs
* User files (saves and settings) stored in GTA root directory
* Settings stored in re3.ini file instead of gta3.set
* Debug menu to do and change various things (Ctrl-M to open)
* Debug camera (Ctrl-B to toggle)
* Rotatable camera
* XInput controller support (Windows)
* No loading screens between islands ("map memory usage" in menu)
* Skinned ped support (models from Xbox or Mobile)
* Rendering
* Widescreen support (properly scaled HUD, Menu and FOV)
* PS2 MatFX (vehicle reflections)
* PS2 alpha test (better rendering of transparency)
* PS2 particles
* Xbox vehicle rendering
* Xbox world lightmap rendering (needs Xbox map)
* Xbox ped rim light
* Xbox screen rain droplets
* More customizable colourfilter
* Menu
* Map
* More options
* Controller configuration menu
* ...
* Can load DFFs and TXDs from other platforms, possibly with a performance penalty
* ...
## To-Do
The following things would be nice to have/do:
* Fix physics for high FPS
* Improve performance on lower end devices, especially the OpenGL layer on the Raspberry Pi (if you have experience with this, please get in touch)
* Compare code with PS2 code (tedious, no good decompiler)
* [PS2 port](https://web.archive.org/web/20210217192931/https://github.com/GTAmodding/re3/wiki/PS2-port)
* Xbox port (not quite as important)
* reverse remaining unused/debug functions
* compare CodeWarrior build with original binary for more accurate code (very tedious)
## Modding
Asset modifications (models, texture, handling, script, ...) should work the same way as with original GTA for the most part.
CLEO scripts work with [CLEO Redux](https://github.com/cleolibrary/CLEO-Redux).
Mods that make changes to the code (dll/asi, limit adjusters) will *not* work.
Some things these mods do are already implemented in re3 (much of SkyGFX, GInput, SilentPatch, Widescreen fix),
others can easily be achieved (increasing limis, see `config.h`),
others will simply have to be rewritten and integrated into the code directly.
Sorry for the inconvenience.
## Building from Source
When using premake, you may want to point GTA_III_RE_DIR environment variable to GTA3 root folder if you want the executable to be moved there via post-build script.
Clone the repository with `git clone --recursive https://github.com/halpz/re3.git`. Then `cd re3` into the cloned repository.
<details><summary>Linux Premake</summary>
For Linux using premake, proceed: [Building on Linux](https://web.archive.org/web/20210217192751/https://github.com/GTAmodding/re3/wiki/Building-on-Linux)
</details>
<details><summary>Linux Conan</summary>
Install python and conan, and then run build.
```
conan export vendor/librw librw/master@
mkdir build
cd build
conan install .. re3/master@ -if build -o re3:audio=openal -o librw:platform=gl3 -o librw:gl3_gfxlib=glfw --build missing -s re3:build_type=RelWithDebInfo -s librw:build_type=RelWithDebInfo
conan build .. -if build -bf build -pf package
```
</details>
<details><summary>MacOS Premake</summary>
For MacOS using premake, proceed: [Building on MacOS](https://web.archive.org/web/20210717004757/https://github.com/GTAmodding/re3/wiki/Building-on-MacOS)
</details>
<details><summary>FreeBSD</summary>
For FreeBSD using premake, proceed: [Building on FreeBSD](https://web.archive.org/web/20210217192740/https://github.com/GTAmodding/re3/wiki/Building-on-FreeBSD)
</details>
<details><summary>Windows</summary>
Assuming you have Visual Studio 2015/2017/2019:
- Run one of the `premake-vsXXXX.cmd` variants on root folder.
- Open build/re3.sln with Visual Studio and compile the solution.
Microsoft recently discontinued its downloads of the DX9 SDK. You can download an archived version here: https://archive.org/details/dxsdk_jun10
**If you choose OpenAL on Windows** You must read [Running OpenAL build on Windows](https://web.archive.org/web/20210217192855/https://github.com/GTAmodding/re3/wiki/Running-OpenAL-build-on-Windows).
</details>
> :information_source: premake has an `--with-lto` option if you want the project to be compiled with Link Time Optimization.
> :information_source: There are various settings in [config.h](https://github.com/halpz/re3/tree/master/src/core/config.h), you may want to take a look there.
> :information_source: re3 uses completely homebrew RenderWare-replacement rendering engine; [librw](https://github.com/aap/librw/). librw comes as submodule of re3, but you also can use LIBRW enviorenment variable to specify path to your own librw.
If you feel the need, you can also use CodeWarrior 7 to compile re3 using the supplied codewarrior/re3.mcp project - this requires the original RW33 libraries, and the DX8 SDK. The build is unstable compared to the MSVC builds though, and is mostly meant to serve as a reference.
## Contributing
As long as it's not linux/cross-platform skeleton/compatibility layer, all of the code on the repo that's not behind a preprocessor condition(like FIX_BUGS) are **completely** reversed code from original binaries.
We **don't** accept custom codes, as long as it's not wrapped via preprocessor conditions, or it's linux/cross-platform skeleton/compatibility layer.
We accept only these kinds of PRs;
- A new feature that exists in at least one of the GTAs (if it wasn't in III/VC then it doesn't have to be decompilation)
- Game, UI or UX bug fixes (if it's a fix to original code, it should be behind FIX_BUGS)
- Platform-specific and/or unused code that's not been reversed yet
- Makes reversed code more understandable/accurate, as in "which code would produce this assembly".
- A new cross-platform skeleton/compatibility layer, or improvements to them
- Translation fixes, for languages original game supported
- Code that increase maintainability
We have a [Coding Style](https://github.com/halpz/re3/blob/master/CODING_STYLE.md) document that isn't followed or enforced very well.
Do not use features from C++11 or later.
## History
re3 was started sometime in the spring of 2018,
initially as a way to test reversed collision and physics code
inside the game.
This was done by replacing single functions of the game
with their reversed counterparts using a dll.
After a bit of work the project lay dormant for about a year
and was picked up again and pushed to github in May 2019.
At the time I (aap) had reversed around 10k lines of code and estimated
the final game to have around 200-250k.
Others quickly joined the effort (Fire_Head, shfil, erorcun and Nick007J
in time order, and Serge a bit later) and we made very quick progress
throughout the summer of 2019
after which the pace slowed down a bit.
Due to everyone staying home during the start of the Corona pandemic
everybody had a lot of time to work on re3 again and
we finally got a standalone exe in April 2020 (around 180k lines by then).
After the initial excitement and fixing and polishing the code further,
reVC was started in early May 2020 by starting from re3 code,
not by starting from scratch replacing functions with a dll.
After a few months of mostly steady progress we considered reVC
finished in December.
Since then we have started reLCS, which is currently work in progress.
You need to have installed:
- GTA III PC
- [DreamSDK R3](https://github.com/dreamsdk/dreamsdk/releases/tag/r3-3.0.4.2311)
- more instructions to follow soon
## License
We don't feel like we're in a position to give this code a license.\
The code should only be used for educational, documentation and modding purposes.\
We do not encourage piracy or commercial use.\
Please keep derivate work open source and give proper credit.
Please keep derivate work open source and give proper credit.

BIN
dreamcast/GTA3SF8.b Normal file

Binary file not shown.

489
dreamcast/Makefile Normal file
View File

@ -0,0 +1,489 @@
#
# Basic KallistiOS skeleton / test program
# Copyright (C)2001-2004 Megan Potter
#
# Put the filename of the output binary here
PROJECT_NAME = dca3
TARGET ?= dca3.elf
HAVE_CDI4DC := $(shell which cdi4dc > /dev/null 2>&1 && echo "yes" || echo "no")
IS_MAC := $(shell uname -s | grep -i "darwin" > /dev/null && echo "yes" || echo "no")
MOD_NAME?=
GTA_DIR?=../../gta3
GTA_MOD_DIR?=../../gta3_mod$(MOD_NAME)
GTA_MOD_IMG_DIR?=$(GTA_MOD_DIR)/img
GTA_MOD_SFX_DIR?=$(GTA_MOD_DIR)/sfx
GTA_MOD_LOOSE_DIR?=$(GTA_MOD_DIR)/loose
REPACK_DIR?=repack-data
REPACK_GTA_DIR?=$(REPACK_DIR)/gta3
REPACK_IMG_ORIG_DIR?=$(REPACK_DIR)/img-orig
REPACK_IMG_DC_DIR?=$(REPACK_DIR)/img-dc
REPACK_SFX_ORIG_DIR?=$(REPACK_DIR)/sfx-orig
REPACK_SFX_DC_DIR?=$(REPACK_DIR)/sfx-dc
REPACK_STREAM_DECODED_DIR?=$(REPACK_DIR)/stream-decoded
LIBS :=
TEXCONV_FLAGS :=
ifeq ($(IS_MAC), yes)
TEXCONV_FLAGS += -DMACOS64
endif
AUDIO_STREAM_OPTION=-t
MKDCDISC_PAD_OPTION=-N
ifeq ($(FOR_DISC),1)
AUDIO_STREAM_OPTION=-q
MKDCDISC_PAD_OPTION=
endif
include common.mk
OBJS = $(RE3_OBJS) $(RW_OBJS) \
../src/audio/sampman_dc.o \
../src/prof/profiler.o
OBJS_TEXCONV = $(RW_OBJS:.o=.texconv.o)
OBJS_TEXCONV += \
../vendor/koshle/hlekos.texconv.o \
../vendor/koshle/hlepvr_mem.texconv.o \
../vendor/koshle/hlepvr_prim.texconv.o \
../vendor/koshle/hlepvr_scene.texconv.o \
../vendor/koshle/hlepvr_misc.texconv.o \
../vendor/koshle/hlepvr_init_term.texconv.o \
../vendor/koshle/hlepvr_buffers.texconv.o \
../vendor/koshle/hlepvr_irq.texconv.o \
../vendor/koshle/hlematrix3d.texconv.o \
../vendor/librw/src/dc/vq.texconv.o \
../src/fakerw/fake.texconv.o \
../src/skel/crossplatform.texconv.o \
../src/rw/TxdStore.texconv.o \
texconv.texconv.o \
../vendor/TriStripper/src/connectivity_graph.texconv.o \
../vendor/TriStripper/src/policy.texconv.o \
../vendor/TriStripper/src/tri_stripper.texconv.o \
../src/rw/VisibilityPlugins.texconv.o \
../src/rw/NodeName.texconv.o \
../src/animation/RpAnimBlend.texconv.o \
../src/animation/Bones.texconv.o \
../src/animation/AnimBlendAssociation.texconv.o \
../src/animation/AnimBlendNode.texconv.o \
../src/animation/AnimBlendClumpData.texconv.o \
../src/rw/MemoryMgr.texconv.o \
../src/math/Quaternion.texconv.o \
../vendor/librw/src/d3d-x/d3d.texconv.o \
../vendor/librw/src/d3d-x/d3d8.texconv.o \
../vendor/librw/src/d3d-x/d3d8render.texconv.o \
# Add compilation units to this list to explicity compile them with
# -O3 optimizations, while the rest get the default (-Os) treatment
# to conserve RAM.
OBJS_O3 = \
../vendor/librw/src/dc/rwdc.o \
../src/core/World.o \
../src/collision/Collision.o \
../src/math/math.o \
../src/math/Matrix.o \
../src/math/Quaternion.o \
../src/math/Rect.o \
../src/math/Vector.o \
../vendor/librw/src/base.o \
../src/renderer/Shadows.o
OBJS_NO_FAST_MATH = \
../src/core/Cam.o \
../src/core/Camera.o
KOS_CPPFLAGS += -fbuiltin -ffast-math -ffp-contract=fast \
-mfsrra -mfsca
# The rm-elf step is to remove the target before building, to force the
# re-creation of the rom disk.
all: $(TARGET)
include $(KOS_BASE)/Makefile.rules
DEPS = $(OBJS:.o=.d) $(OBJS_TEXCONV:.o:.d)
CXXFLAGS += $(if $(WITH_32MB),-O3,-Os) \
$(if $(WITH_IDE),-DWITH_IDE) \
$(if $(WITH_PROF),-DWITH_PROF=\"$(WITH_PROF)\") \
-MMD -MP -ffunction-sections -fdata-sections -ffast-math \
-fmerge-all-constants -fomit-frame-pointer -ml -std=gnu++20 \
-fno-exceptions -fno-rtti -flto=auto -fipa-pta -Wno-write-strings \
-Wno-deprecated-enum-enum-conversion -Wno-deprecated-enum-float-conversion \
-Wno-multichar -Wno-unused-value -Wno-char-subscripts -Wno-reorder \
-Wno-unused-function -Wno-class-memaccess -fno-permissive \
-fno-asynchronous-unwind-tables -fno-enforce-eh-specs -fno-non-call-exceptions \
-fno-strict-aliasing -fwrapv
clean-texconv:
-rm -f $(OBJS_TEXCONV)
-rm -f texconv
clean-pvrtex:
$(MAKE) -C ./pvrtex clean
-rm -f ./pvrtex/pvrtex
clean-objs:
-rm -f $(OBJS)
clean:
-rm -f $(OBJS)
-rm -f $(OBJS_TEXCONV)
-rm -f $(TARGET)
-rm -f $(TARGET).bin
-rm -f 1ST_READ.BIN
-rm -f IP.BIN
-rm -f $(PROJECT_NAME).iso
-rm -f $(PROJECT_NAME).ds.iso
-rm -f $(PROJECT_NAME).cdi
-rm -f $(DEPS)
-rm -rf $(REPACK_DIR)
-rm -rf analyze-profile
$(OBJS_O3): %.o: %.cpp
kos-c++ $(CXXFLAGS) $(CPPFLAGS) -O3 -c $< -o $@
$(OBJS_NO_FAST_MATH): %.o: %.cpp
kos-c++ $(CXXFLAGS) $(CPPFLAGS) -O3 -c $< -o $@ -fno-fast-math
$(TARGET): $(OBJS)
kos-c++ -o $(TARGET) $(OBJS) -Wl,--gc-sections -Wl,--as-needed -Wl,-Map,output.map \
-flto=auto $(if $(WITH_IDE),-lkosfat) $(if $(WITH_SD),-lkosfat)
@echo && echo && echo "*** Build Completed Successfully ***" && echo && echo
run: $(TARGET)
$(KOS_LOADER) $(TARGET)
$(REPACK_GTA_DIR)/GTA3SF8.b: GTA3SF8.b
mkdir -p $(@D)
cp $< $@
1ST_READ.BIN: $(TARGET)
rm -f $(TARGET).bin
rm -f 1ST_READ.BIN
kos-objcopy -R .stack -O binary $(TARGET) $(TARGET).bin
$(KOS_BASE)/utils/scramble/scramble $(TARGET).bin 1ST_READ.BIN
.PHONY: pvrtex
pvrtex:
$(MAKE) -C ./pvrtex
IP.BIN:
rm -f IP.BIN
$(KOS_BASE)/utils/makeip/makeip ip.txt IP.BIN
$(PROJECT_NAME).iso: IP.BIN 1ST_READ.BIN $(REPACK_DIR)/repacked $(REPACK_GTA_DIR)/GTA3SF8.b
rm -f $(PROJECT_NAME).iso
rm -f $(REPACK_GTA_DIR)/1ST_READ.BIN
cp 1ST_READ.BIN $(REPACK_GTA_DIR)
mkisofs -C 0,11702 -V $(PROJECT_NAME) -G IP.BIN -r -J -l -o $(PROJECT_NAME).iso $(REPACK_GTA_DIR)
$(PROJECT_NAME)-no-repack.iso: IP.BIN 1ST_READ.BIN $(REPACK_GTA_DIR)/GTA3SF8.b
rm -f $(PROJECT_NAME)-no-repack.iso
rm -f $(REPACK_GTA_DIR)/1ST_READ.BIN
cp 1ST_READ.BIN $(REPACK_GTA_DIR)
mkisofs -C 0,11702 -V $(PROJECT_NAME) -G IP.BIN -r -J -l -o $(PROJECT_NAME)-no-repack.iso $(REPACK_GTA_DIR)
$(PROJECT_NAME).ds.iso: IP.BIN 1ST_READ.BIN $(REPACK_DIR)/repacked $(REPACK_GTA_DIR)/GTA3SF8.b
rm -f $(PROJECT_NAME).ds.iso
rm -f $(REPACK_GTA_DIR)/1ST_READ.BIN
cp $(TARGET).bin $(REPACK_GTA_DIR)/1ST_READ.BIN
mkisofs -V $(PROJECT_NAME) -G IP.BIN -r -J -l -o $(PROJECT_NAME).ds.iso $(REPACK_GTA_DIR)
1ST_READ_PREBUILT.BIN:
kos-objcopy -R .stack -O binary $(TARGET) $(TARGET)-prebuilt.bin
$(KOS_BASE)/utils/scramble/scramble $(TARGET)-prebuilt.bin 1ST_READ_PREBUILT.BIN
mkdir -p $(REPACK_GTA_DIR)
$(PROJECT_NAME)-prebuilt.iso: IP.BIN 1ST_READ_PREBUILT.BIN $(REPACK_DIR)/repacked $(REPACK_GTA_DIR)/GTA3SF8.b
rm -f $(REPACK_GTA_DIR)/1ST_READ.BIN
cp 1ST_READ_PREBUILT.BIN $(REPACK_GTA_DIR)/1ST_READ.BIN
mkisofs -C 0,11702 -V $(PROJECT_NAME) -G IP.BIN -r -J -l -o $(PROJECT_NAME).iso $(REPACK_GTA_DIR)
ifeq ($(HAVE_CDI4DC), yes)
$(PROJECT_NAME).cdi: $(PROJECT_NAME).iso
cdi4dc $(PROJECT_NAME).iso $(PROJECT_NAME).cdi > cdi.log
$(PROJECT_NAME)-no-repack.cdi: $(PROJECT_NAME)-no-repack.iso
cdi4dc $(PROJECT_NAME)-no-repack.iso $(PROJECT_NAME)-no-repack.cdi > cdi.log
$(PROJECT_NAME)-prebuilt.cdi: $(PROJECT_NAME)-prebuilt.iso
cdi4dc $(PROJECT_NAME).iso $(PROJECT_NAME).cdi > cdi.log
rm 1ST_READ_PREBUILT.BIN
else
$(PROJECT_NAME).cdi: $(TARGET) $(REPACK_DIR)/repacked $(REPACK_GTA_DIR)/GTA3SF8.b
mkdcdisc -e $(TARGET) -o $(PROJECT_NAME).cdi -d $(REPACK_GTA_DIR)/ $(MKDCDISC_PAD_OPTION) -n DCA3 -a "the gang"
$(PROJECT_NAME)-no-repack.cdi: $(TARGET) $(REPACK_GTA_DIR)/GTA3SF8.b
mkdcdisc -e $(TARGET) -o $(PROJECT_NAME)-no-repack.cdi -d $(REPACK_GTA_DIR)/ $(MKDCDISC_PAD_OPTION) -n DCA3 -a "the gang"
$(PROJECT_NAME)-prebuilt.cdi: $(REPACK_DIR)/repacked $(REPACK_GTA_DIR)/GTA3SF8.b
mkdcdisc -e $(TARGET) -o $(PROJECT_NAME).cdi -d $(REPACK_GTA_DIR)/ $(MKDCDISC_PAD_OPTION) -n DCA3 -a "the gang"
endif
cdi: $(PROJECT_NAME).cdi
cdi-no-repack: $(PROJECT_NAME)-no-repack.cdi
dsiso: $(PROJECT_NAME).ds.iso
cdi-prebuilt: $(PROJECT_NAME)-prebuilt.cdi
sim: $(REPACK_DIR)/repacked
$(MAKE) -f sim.mk
run-flycast:
flycast ./$(PROJECT_NAME).cdi
repack-dc: $(REPACK_DIR)/repacked
gprof:
@sh-elf-gprof $(TARGET) $(REPACK_DIR)/kernel_gmon_1.out > gprof.out
@cat gprof.out | gprof2dot | dot -Tpng -o $(TARGET)-kernel.png
@-rm -rf gprof.out
@echo "\033[42m Profiling data saved to $(TARGET)-kernel.png \033[0m"
# tools
imgtool: imgtool.cpp
$(CXX) -std=c++17 -o $@ -Og $<
extract-sfx: extract-sfx.cpp
$(CXX) -std=c++17 -o $@ -Og $<
pack-sfx: pack-sfx.cpp
$(CXX) -std=c++17 -o $@ -Og $<
aud2adpcm: aud2adpcm.c
$(CC) -o $@ -O3 -g $< -I../vendor/minimp3
texconv: $(OBJS_TEXCONV) | pvrtex # You'll have to rebuild pvrtex manually if you change it
$(CXX) -o $@ $(OBJS_TEXCONV)
%.texconv.o: %.cpp
$(CXX) -std=c++2a -c -O3 -g -MMD -MP -o $@ -I../vendor/koshle $(INCLUDE) -I../vendor/emu -I../vendor/crypto -I../vendor/TriStripper/include $(DEFINES) -DDC_TEXCONV -DDC_SIM -D_INC_WINDOWS $(TEXCONV_FLAGS) $<
-include $(DEPS)
#### Repacking ####
TXD_OPTS_fonts = 256 256
TXD_OPTS_hud = 128 128
TXD_OPTS_menu = 512 512
TXD_OPTS_LOADSC0 = 512 512
TXD_OPTS_LOADSC1 = 512 512
TXD_OPTS_LOADSC10 = 512 512
TXD_OPTS_LOADSC11 = 512 512
TXD_OPTS_LOADSC12 = 512 512
TXD_OPTS_LOADSC13 = 512 512
TXD_OPTS_LOADSC14 = 512 512
TXD_OPTS_LOADSC15 = 512 512
TXD_OPTS_LOADSC16 = 512 512
TXD_OPTS_LOADSC17 = 512 512
TXD_OPTS_LOADSC18 = 512 512
TXD_OPTS_LOADSC19 = 512 512
TXD_OPTS_LOADSC2 = 512 512
TXD_OPTS_LOADSC20 = 512 512
TXD_OPTS_LOADSC21 = 512 512
TXD_OPTS_LOADSC22 = 512 512
TXD_OPTS_LOADSC23 = 512 512
TXD_OPTS_LOADSC24 = 512 512
TXD_OPTS_LOADSC25 = 512 512
TXD_OPTS_LOADSC3 = 512 512
TXD_OPTS_LOADSC4 = 512 512
TXD_OPTS_LOADSC5 = 512 512
TXD_OPTS_LOADSC6 = 512 512
TXD_OPTS_LOADSC7 = 512 512
TXD_OPTS_LOADSC8 = 512 512
TXD_OPTS_LOADSC9 = 512 512
TXD_OPTS_mainsc1 = 512 512
TXD_OPTS_mainsc2 = 512 512
TXD_OPTS_NEWS = 512 512
TXD_OPTS_SPLASH1 = 512 512
TXD_OPTS_SPLASH2 = 512 512
TXD_OPTS_SPLASH3 = 512 512
DEFAULT_RES = 512
PVR_ENCODER ?= PVRTEX
TEXTURE_DOWNSAMPLE_TXD ?= NONE
TEXTURE_DOWNSAMPLE_IMG ?= HALF
-include texlist.mk
-include modlist.mk
-include gta3files.mk
-include sfxlist.mk
-include sfxlooplist.mk
-include wavlist.mk
-include mp3list.mk
IMG_TEXTURES_DC = $(addprefix $(REPACK_IMG_DC_DIR)/, $(IMG_TEXTURES))
IMG_MODELS_DC = $(addprefix $(REPACK_IMG_DC_DIR)/, $(IMG_MODELS))
LOOSE_FILES_DC = $(addprefix $(REPACK_GTA_DIR)/, $(MISC_FILES))
SFX_DC_DIR = $(REPACK_GTA_DIR)/sfx
SFX_DC_RAW = $(SFX_DC_DIR)/sfx_all.raw
SFX_DC_DSC = $(SFX_DC_DIR)/sfx_all.dsc
STREAM_ADPCM_DC = $(addprefix $(REPACK_GTA_DIR)/stream/, $(STREAM_WAV:.wav=.APM)) \
$(addprefix $(REPACK_GTA_DIR)/stream/, $(STREAM_MP3:.mp3=.APM))
IMG_TEXTURES_ORIG = $(addprefix $(REPACK_IMG_ORIG_DIR)/, $(IMG_TEXTURES))
IMG_MODELS_ORIG = $(addprefix $(REPACK_IMG_ORIG_DIR)/, $(IMG_MODELS))
SFX_ORIG = $(addprefix $(REPACK_SFX_ORIG_DIR)/, $(SFX_WAV))
SFX_ORIG_LOOP = $(addprefix $(REPACK_SFX_ORIG_DIR)/, $(SFX_LOOP_WAV))
SFX_REPACK_DC_WAV = $(addprefix $(REPACK_SFX_DC_DIR)/, $(SFX_WAV) $(SFX_LOOP_WAV))
SFX_REPACK_DC = $(SFX_REPACK_DC_WAV:.wav=.pcm)
STREAM_MP3_DECODED = $(addprefix $(REPACK_STREAM_DECODED_DIR)/, $(STREAM_MP3:.mp3=.wav))
STREAM_WAV_DECODED = $(addprefix $(REPACK_STREAM_DECODED_DIR)/, $(STREAM_WAV))
.PRECIOUS: $(SFX_ORIG) $(SFX_REPACK_DC) $(STREAM_MP3_DECODED) $(STREAM_WAV_DECODED)
$(REPACK_DIR)/repacked: $(REPACK_GTA_DIR)/models/gta3.img $(REPACK_GTA_DIR)/models/gta3.dir $(LOOSE_FILES_DC) $(STREAM_ADPCM_DC) $(SFX_DC_RAW) $(SFX_DC_DSC)
mkdir -p $(@D)
touch $@
@echo && echo && echo "*** Repack Completed Successfully ***" && echo && echo
$(REPACK_DIR)/unpacked: imgtool $(GTA_DIR)/models/gta3.img $(GTA_DIR)/models/gta3.dir
mkdir -p $(@D)
./imgtool unpack "$(GTA_DIR)/models/gta3" "$(REPACK_IMG_ORIG_DIR)"
@touch $@
$(IMG_TEXTURES_ORIG) $(IMG_MODELS_ORIG): $(REPACK_DIR)/unpacked
@touch $@
# First try the mods img directory
$(REPACK_IMG_DC_DIR)/%.dff: $(GTA_MOD_IMG_DIR)/%.dff texconv
@mkdir -p $(@D)
./texconv $< $@
$(REPACK_IMG_DC_DIR)/%.DFF: $(GTA_MOD_IMG_DIR)/%.DFF texconv
@mkdir -p $(@D)
./texconv $< $@
# if not, the extracted img directory
$(REPACK_IMG_DC_DIR)/%.dff: $(REPACK_IMG_ORIG_DIR)/%.dff texconv
@mkdir -p $(@D)
./texconv $< $@
$(REPACK_IMG_DC_DIR)/%.DFF: $(REPACK_IMG_ORIG_DIR)/%.DFF texconv
@mkdir -p $(@D)
./texconv $< $@
# first try the mods img directory. NB, the textures are not resized here, unlike normal .img textures
$(REPACK_IMG_DC_DIR)/%.txd: $(GTA_MOD_IMG_DIR)/%.txd texconv
@mkdir -p $(@D)
./texconv $< $@ 1024 1024 -e $(PVR_ENCODER) -d NONE
$(REPACK_IMG_DC_DIR)/%.TXD: $(GTA_MOD_IMG_DIR)/%.TXD texconv
@mkdir -p $(@D)
./texconv $< $@ 1024 1024 -e $(PVR_ENCODER) -d NONE
# if not, the extracted img directory
$(REPACK_IMG_DC_DIR)/%.txd: $(REPACK_IMG_ORIG_DIR)/%.txd texconv
@mkdir -p $(@D)
./texconv $< $@ $(DEFAULT_RES) $(DEFAULT_RES) -e $(PVR_ENCODER) -d $(TEXTURE_DOWNSAMPLE_IMG)
$(REPACK_IMG_DC_DIR)/%.TXD: $(REPACK_IMG_ORIG_DIR)/%.TXD texconv
@mkdir -p $(@D)
./texconv $< $@ $(DEFAULT_RES) $(DEFAULT_RES) -e $(PVR_ENCODER) -d $(TEXTURE_DOWNSAMPLE_IMG)
# first try the mods loose directory
$(REPACK_GTA_DIR)/%.dff: $(GTA_MOD_LOOSE_DIR)/%.dff texconv
@mkdir -p $(@D)
./texconv $< $@
$(REPACK_GTA_DIR)/%.DFF: $(GTA_MOD_LOOSE_DIR)/%.DFF texconv
@mkdir -p $(@D)
./texconv $< $@
#if not, the original files
$(REPACK_GTA_DIR)/%.dff: $(GTA_DIR)/%.dff texconv
@mkdir -p $(@D)
./texconv $< $@
$(REPACK_GTA_DIR)/%.DFF: $(GTA_DIR)/%.DFF texconv
@mkdir -p $(@D)
./texconv $< $@
# first try the mods loose directory
# Note the mods loose directory is not resized, unlike the normal .txd textures
$(REPACK_GTA_DIR)/%.txd: $(GTA_MOD_LOOSE_DIR)/%.txd texconv
@mkdir -p $(@D)
./texconv $< $@ 1024 1024 -e $(PVR_ENCODER) -d NONE
$(REPACK_GTA_DIR)/%.TXD: $(GTA_MOD_LOOSE_DIR)/%.TXD texconv
@mkdir -p $(@D)
./texconv $< $@ 1024 1024 -e $(PVR_ENCODER) -d NONE
# if not, the original files
$(REPACK_GTA_DIR)/%.txd: $(GTA_DIR)/%.txd texconv
@mkdir -p $(@D)
./texconv $< $@ $(TXD_OPTS_$(notdir $*)) -e $(PVR_ENCODER) -d $(TEXTURE_DOWNSAMPLE_TXD)
$(REPACK_GTA_DIR)/%.TXD: $(GTA_DIR)/%.TXD texconv
@mkdir -p $(@D)
./texconv $< $@ $(TXD_OPTS_$(notdir $*)) -e $(PVR_ENCODER) -d $(TEXTURE_DOWNSAMPLE_TXD)
$(REPACK_DIR)/packed: $(IMG_TEXTURES_DC) $(IMG_MODELS_DC)
mkdir -p $(@D)
mkdir -p "$(REPACK_GTA_DIR)/models/gta3"
./imgtool pack "$(REPACK_GTA_DIR)/models/gta3" "$(REPACK_IMG_DC_DIR)"
touch $@
$(REPACK_GTA_DIR)/models/gta3.img $(REPACK_GTA_DIR)/models/gta3.dir: $(REPACK_DIR)/packed
touch $@
# sfx processing
$(REPACK_DIR)/unpacked-sfx: extract-sfx $(GTA_DIR)/audio/sfx.SDT $(GTA_DIR)/audio/sfx.RAW
mkdir -p $(@D)
mkdir -p "$(REPACK_SFX_ORIG_DIR)"
./extract-sfx "$(GTA_DIR)/audio/sfx.SDT" "$(GTA_DIR)/audio/sfx.RAW" "$(REPACK_SFX_ORIG_DIR)"
touch $@
$(SFX_ORIG): $(REPACK_DIR)/unpacked-sfx
touch $@
$(SFX_ORIG_LOOP): $(SFX_ORIG)
@touch $@
# try first mods sfx directory
$(REPACK_SFX_DC_DIR)/%.pcm: $(GTA_MOD_SFX_DIR)/%.wav aud2adpcm
@mkdir -p $(@D)
./aud2adpcm -raw $< $@
# then original (extracted) sfx directory
$(REPACK_SFX_DC_DIR)/%.pcm: $(REPACK_SFX_ORIG_DIR)/%.wav aud2adpcm
@mkdir -p $(@D)
./aud2adpcm -raw $< $@
# stream processing
# first try the mods loose directory
$(REPACK_GTA_DIR)/stream/%.APM: $(GTA_MOD_LOOSE_DIR)/audio/%.wav aud2adpcm
@mkdir -p $(@D)
./aud2adpcm $(AUDIO_STREAM_OPTION) $< $@
$(REPACK_GTA_DIR)/stream/%.APM: $(GTA_MOD_LOOSE_DIR)/audio/%.mp3 aud2adpcm
@mkdir -p $(@D)
./aud2adpcm $(AUDIO_STREAM_OPTION) $< $@
# then original folder
$(REPACK_GTA_DIR)/stream/%.APM: $(GTA_DIR)/audio/%.wav aud2adpcm
@mkdir -p $(@D)
./aud2adpcm $(AUDIO_STREAM_OPTION) $< $@
$(REPACK_GTA_DIR)/stream/%.APM: $(GTA_DIR)/audio/%.mp3 aud2adpcm
@mkdir -p $(@D)
./aud2adpcm $(AUDIO_STREAM_OPTION) $< $@
# Note: This is last so it has least priority, files should be processed if possible
$(REPACK_GTA_DIR)/%: $(GTA_DIR)/%
@mkdir -p $(@D)
cp $< $@
$(SFX_DC_RAW): pack-sfx $(SFX_REPACK_DC)
mkdir -p $(@D)
./pack-sfx "$(GTA_DIR)/audio/sfx.SDT" $(SFX_DC_RAW) $(SFX_DC_DSC) $(REPACK_SFX_DC_DIR)
$(SFX_DC_DSC): $(SFX_DC_RAW)
ls -l $@
analyze-profile: analyze-profile.cpp
$(CXX) -std=c++17 -O3 analyze-profile.cpp -o analyze-profile

View File

@ -0,0 +1,569 @@
#include <iostream>
#include <fstream>
#include <cstdint>
#include <vector>
#include <cstring>
#include <algorithm>
#include <unordered_map>
#include <sstream>
#include <iomanip>
#include <regex>
#include <set>
#include <unordered_set>
#include <functional>
#include <cxxabi.h> // For demangling
#define SAMPLE_PERCENTAGE_THRESHOLD 0.1 // Threshold for including functions
struct GmonHeader {
char cookie[4]; // 'g','m','o','n'
int32_t version; // 1
char spare[12]; // Padding
};
struct GmonArc {
uint32_t from_pc; // Address within caller's body
uint32_t self_pc; // Address within callee's body
uint32_t count; // Number of arc traversals
};
struct FunctionInfo {
std::string mangled_name; // Mangled function name
std::string demangled_name; // Demangled function name
uint32_t start_address;
uint32_t end_address; // Will be updated as we parse the source file
uint64_t sample_count;
double sample_percentage; // Percentage of total samples
uint64_t samples_with_children; // Total samples including callees
std::unordered_map<uint32_t, uint64_t> callers; // Set of caller addresses
};
// Updated demangle_symbol function using abi::__cxa_demangle
std::string demangle_symbol(const std::string& mangled_name) {
static std::unordered_map<std::string, std::string> demangle_cache;
// Check if the name is already in the cache
auto cache_it = demangle_cache.find(mangled_name);
if (cache_it != demangle_cache.end()) {
return cache_it->second;
}
int status = 0;
char* demangled = abi::__cxa_demangle(mangled_name.c_str(), nullptr, nullptr, &status);
std::string demangled_name;
if (status == 0 && demangled != nullptr) {
demangled_name = demangled;
free(demangled);
} else {
// If demangling fails, return the mangled name
demangled_name = mangled_name;
}
demangle_cache[mangled_name] = demangled_name;
return demangled_name;
}
int main(int argc, char* argv[]) {
// Enable synchronization between C++ and C I/O
std::ios::sync_with_stdio(false);
// Check for the required arguments
if (argc < 3) {
std::cerr << "Usage: " << argv[0] << " <gmon_file> <source_dump_file>" << std::endl;
return 1;
}
const char* gmon_filename = argv[1]; // Profiler output file
const char* source_filename = argv[2]; // Source dump file
std::cout << "[*] Starting profiling data analysis..." << std::endl;
// Open the profiler output file
std::cout << "[*] Reading profiler output file: " << gmon_filename << std::endl;
std::ifstream infile(gmon_filename, std::ios::binary);
if (!infile) {
std::cerr << "Error opening file " << gmon_filename << std::endl;
return 1;
}
// Read GmonHeader
GmonHeader header;
infile.read(reinterpret_cast<char*>(&header), sizeof(GmonHeader));
// Verify the header
if (std::strncmp(header.cookie, "gmon", 4) != 0) {
std::cerr << "Invalid file format: missing 'gmon' cookie" << std::endl;
return 1;
}
// Collect arc records
std::vector<GmonArc> arcs;
while (infile.peek() != EOF) {
uint8_t tag;
infile.read(reinterpret_cast<char*>(&tag), sizeof(tag));
if (infile.eof()) {
break;
}
if (tag == 1) {
// Arc record
GmonArc arc;
infile.read(reinterpret_cast<char*>(&arc), sizeof(GmonArc));
arcs.push_back(arc);
} else if (tag == 0) {
// Histogram data: skip it
// Read GmonHistHeader to get hist_size
struct {
uint32_t low_pc;
uint32_t high_pc;
uint32_t hist_size;
uint32_t prof_rate;
char dimen[15];
char dimen_abbrev;
} hist_header;
infile.read(reinterpret_cast<char*>(&hist_header), sizeof(hist_header));
// Skip the histogram bins
infile.ignore(hist_header.hist_size * sizeof(uint16_t));
} else {
std::cerr << "Unknown tag encountered: " << static_cast<int>(tag) << std::endl;
break;
}
}
infile.close();
std::cout << "[*] Profiler data reading completed." << std::endl;
// Create a mapping from self_pc to count
std::unordered_map<uint32_t, uint64_t> address_counts;
uint64_t total_samples = 0;
for (const auto& arc : arcs) {
// Sum counts for each address
address_counts[arc.self_pc] += arc.count;
total_samples += arc.count;
}
std::cout << "[*] Total samples collected: " << total_samples << std::endl;
// Open the source dump file
std::cout << "[*] Processing source dump file: " << source_filename << std::endl;
std::ifstream source_file(source_filename);
if (!source_file) {
std::cerr << "Error opening source dump file " << source_filename << std::endl;
return 1;
}
// Prepare the output annotated file
std::string output_filename = std::string(source_filename) + ".annotated";
std::ofstream annotated_file(output_filename);
if (!annotated_file) {
std::cerr << "Error creating annotated file " << output_filename << std::endl;
return 1;
}
// First pass: Identify functions and their ranges
std::cout << "[*] Identifying functions and their address ranges..." << std::endl;
std::string line;
FunctionInfo current_function;
current_function.start_address = 0;
current_function.end_address = 0;
current_function.sample_count = 0;
std::vector<FunctionInfo> functions;
uint32_t last_address = 0;
while (std::getline(source_file, line)) {
// Regular expression to match function headers
// Pattern: address <function_name>:
std::regex function_header_regex(R"(^\s*([0-9a-fA-F]+)\s+<_(.*)>:)");
std::smatch match;
if (std::regex_search(line, match, function_header_regex)) {
uint32_t address = 0;
std::istringstream addr_ss(match[1]);
addr_ss >> std::hex >> address;
std::string function_name = match[2];
// Use address as name if function_name is empty
if (function_name.empty()) {
function_name = "0x" + match[1].str();
}
// Finish the previous function
if (current_function.start_address != 0) {
current_function.end_address = last_address + 1;
functions.push_back(current_function);
}
// Start a new function
current_function.mangled_name = function_name;
current_function.demangled_name = demangle_symbol(function_name);
current_function.start_address = address;
current_function.end_address = 0;
current_function.sample_count = 0;
last_address = address;
continue;
}
// Attempt to parse an address at the beginning of the line
std::istringstream iss(line);
std::string address_str;
iss >> address_str;
// Remove any trailing colon from the address
if (!address_str.empty() && address_str.back() == ':') {
address_str.pop_back();
}
uint32_t address = 0;
std::istringstream addr_ss(address_str);
addr_ss >> std::hex >> address;
if (!addr_ss.fail()) {
last_address = address;
}
}
// Finish the last function
if (current_function.start_address != 0) {
current_function.end_address = UINT32_MAX; // Assume it goes to the end
functions.push_back(current_function);
}
std::cout << "[*] Function identification completed. Total functions found: " << functions.size() << std::endl;
source_file.clear();
source_file.seekg(0, std::ios::beg); // Reset the file stream
// Map addresses to functions
// For efficiency, we can sort the functions and use binary search
std::sort(functions.begin(), functions.end(), [](const FunctionInfo& a, const FunctionInfo& b) {
return a.start_address < b.start_address;
});
// Second pass: Calculate sample counts and percentages per function
std::cout << "[*] Calculating sample counts per function..." << std::endl;
for (auto& func : functions) {
func.sample_count = 0;
func.samples_with_children = 0; // Initialize
}
// Function to find a function by address
auto find_function_by_address = [&](uint32_t address) -> FunctionInfo* {
// Use binary search since functions are sorted by start_address
auto func_it = std::upper_bound(functions.begin(), functions.end(), address, [](uint32_t addr, const FunctionInfo& func) {
return addr < func.start_address;
});
if (func_it != functions.begin()) {
--func_it;
if (address >= func_it->start_address && address < func_it->end_address) {
return &(*func_it);
}
}
return nullptr; // Function not found
};
// Calculate sample counts per function
for (const auto& addr_count : address_counts) {
uint32_t address = addr_count.first;
uint64_t count = addr_count.second;
// Find the function containing this address
FunctionInfo* func = find_function_by_address(address);
if (func) {
func->sample_count += count;
}
}
// Update caller information for each function
for (const auto& arc : arcs) {
FunctionInfo* func = find_function_by_address(arc.self_pc);
if (func) {
func->callers[arc.from_pc] += arc.count;
}
}
// Calculate sample percentages per function
for (auto& func : functions) {
func.sample_percentage = (total_samples > 0) ? (func.sample_count * 100.0) / total_samples : 0.0;
}
std::cout << "[*] Sample count calculation completed." << std::endl;
// Filter out functions with sample_percentage <= 0.1%
functions.erase(std::remove_if(functions.begin(), functions.end(), [](const FunctionInfo& func) {
return func.sample_percentage <= SAMPLE_PERCENTAGE_THRESHOLD;
}), functions.end());
// Build the call graph including only functions with > 0.1% sample_percentage
std::cout << "[*] Building the call graph including only significant functions..." << std::endl;
// Map from FunctionInfo* to callees with arc counts
std::unordered_map<FunctionInfo*, std::unordered_map<FunctionInfo*, uint64_t>> call_graph;
// Process arcs to build the call graph
for (const auto& arc : arcs) {
uint32_t from_address = arc.from_pc;
uint32_t self_address = arc.self_pc;
FunctionInfo* caller_func = find_function_by_address(from_address);
FunctionInfo* callee_func = find_function_by_address(self_address);
if (caller_func && callee_func) {
// Both functions must be significant (sample_percentage > 0.1%)
if (caller_func->sample_percentage > SAMPLE_PERCENTAGE_THRESHOLD &&
callee_func->sample_percentage > SAMPLE_PERCENTAGE_THRESHOLD) {
// Add an edge from caller to callee
call_graph[caller_func][callee_func] += arc.count;
}
}
}
std::cout << "[*] Call graph construction completed." << std::endl;
// Recompute samples_with_children for each function
std::cout << "[*] Computing samples with children for each function..." << std::endl;
std::unordered_map<FunctionInfo*, uint64_t> samples_with_children_map;
std::function<uint64_t(FunctionInfo*, std::unordered_set<FunctionInfo*>&)> compute_samples_with_children =
[&](FunctionInfo* func, std::unordered_set<FunctionInfo*>& visited) -> uint64_t {
if (samples_with_children_map.count(func)) {
return samples_with_children_map[func];
}
if (visited.count(func)) {
// Cycle detected, avoid infinite recursion
return 0;
}
visited.insert(func);
uint64_t total_samples = func->sample_count;
if (call_graph.count(func)) {
for (const auto& callee_entry : call_graph[func]) {
FunctionInfo* callee = callee_entry.first;
total_samples += compute_samples_with_children(callee, visited);
}
}
visited.erase(func);
samples_with_children_map[func] = total_samples;
return total_samples;
};
for (auto& func : functions) {
std::unordered_set<FunctionInfo*> visited;
func.samples_with_children = compute_samples_with_children(&func, visited);
}
std::cout << "[*] Samples with children computation completed." << std::endl;
// Generate the call graph in DOT format
std::cout << "[*] Generating the call graph DOT file..." << std::endl;
std::string dot_filename = std::string(source_filename) + ".graph.dot";
std::ofstream dot_file(dot_filename);
if (!dot_file) {
std::cerr << "Error creating DOT file " << dot_filename << std::endl;
return 1;
}
dot_file << "digraph CallGraph {\n";
dot_file << " node [shape=box];\n";
// Write nodes with labels including samples_with_children%(samples_self%)
for (FunctionInfo* func_ptr = functions.data(); func_ptr != functions.data() + functions.size(); ++func_ptr) {
FunctionInfo& func = *func_ptr;
// Prioritize demangled name; if empty, use mangled name; if empty, use address
std::string function_label;
if (!func.demangled_name.empty() && func.demangled_name != func.mangled_name) {
function_label = func.demangled_name;
} else if (!func.mangled_name.empty()) {
function_label = func.mangled_name;
} else {
std::ostringstream oss;
oss << "0x" << std::hex << func.start_address;
function_label = oss.str();
}
// Label format: samples_with_children%(samples_self%)
double samples_with_children_percentage = (total_samples > 0) ? (func.samples_with_children * 100.0) / total_samples : 0.0;
double samples_self_percentage = (total_samples > 0) ? (func.sample_count * 100.0) / total_samples : 0.0;
std::ostringstream label;
label << function_label << "\\n";
label << std::fixed << std::setprecision(2) << samples_with_children_percentage << "%";
label << " (" << samples_self_percentage << "%)";
// Node identifier can be function's start_address
std::ostringstream node_id_stream;
node_id_stream << "0x" << std::hex << func.start_address;
std::string node_id = node_id_stream.str();
dot_file << " \"" << node_id << "\" [label=\"" << label.str() << "\"];\n";
}
// Write edges with arc counts
for (const auto& caller_entry : call_graph) {
FunctionInfo* caller_func = caller_entry.first;
std::ostringstream caller_id_stream;
caller_id_stream << "0x" << std::hex << caller_func->start_address;
std::string caller_id = caller_id_stream.str();
for (const auto& callee_entry : caller_entry.second) {
FunctionInfo* callee_func = callee_entry.first;
uint64_t arc_count = callee_entry.second;
std::ostringstream callee_id_stream;
callee_id_stream << "0x" << std::hex << callee_func->start_address;
std::string callee_id = callee_id_stream.str();
// Edge from caller_func to callee_func with label arc_count
dot_file << " \"" << caller_id << "\" -> \"" << callee_id << "\" [label=\"" << arc_count << "\"];\n";
}
}
dot_file << "}\n";
dot_file.close();
std::cout << "[*] Call graph DOT file generated: " << dot_filename << std::endl;
// Third pass: Annotate the source file
std::cout << "[*] Annotating the source file..." << std::endl;
source_file.clear();
source_file.seekg(0, std::ios::beg); // Reset the file stream
FunctionInfo* current_function_ptr = nullptr;
while (std::getline(source_file, line)) {
std::string original_line = line; // Keep the original line for output
// Regular expression to match function headers
std::regex function_header_regex(R"(^\s*([0-9a-fA-F]+)\s+<(.*)>:)");
std::smatch match;
if (std::regex_search(line, match, function_header_regex)) {
// Function header line
uint32_t address = 0;
std::istringstream addr_ss(match[1]);
addr_ss >> std::hex >> address;
// Find the function info
current_function_ptr = find_function_by_address(address);
// Annotate the function header line if the function is significant
if (current_function_ptr && current_function_ptr->sample_percentage > SAMPLE_PERCENTAGE_THRESHOLD) {
uint32_t func_samples = current_function_ptr->sample_count;
double func_percentage = current_function_ptr->sample_percentage;
// Prioritize demangled name; if empty, use mangled name; if empty, use address
std::string function_label;
if (!current_function_ptr->demangled_name.empty() && current_function_ptr->demangled_name != current_function_ptr->mangled_name) {
function_label = current_function_ptr->demangled_name;
} else if (!current_function_ptr->mangled_name.empty()) {
function_label = current_function_ptr->mangled_name;
} else {
std::ostringstream oss;
oss << "0x" << std::hex << current_function_ptr->start_address;
function_label = oss.str();
}
annotated_file << original_line << " !!!! " << func_samples << " " << std::fixed << std::setprecision(2) << func_percentage << "% @@@ " << function_label << std::endl;
} else {
// Function is not significant or not found
current_function_ptr = nullptr;
annotated_file << original_line << std::endl;
}
continue;
}
// Attempt to parse an address at the beginning of the line
std::istringstream iss(line);
std::string address_str;
iss >> address_str;
// Remove any trailing colon from the address
if (!address_str.empty() && address_str.back() == ':') {
address_str.pop_back();
}
uint32_t address = 0;
std::istringstream addr_ss(address_str);
addr_ss >> std::hex >> address;
if (addr_ss.fail()) {
// Not a valid address, write the line as is
annotated_file << original_line << std::endl;
continue;
}
// For code lines within significant functions
if (current_function_ptr && current_function_ptr->sample_percentage > SAMPLE_PERCENTAGE_THRESHOLD) {
// Check if the address is in the address_counts map
auto count_it = address_counts.find(address);
if (count_it != address_counts.end()) {
uint32_t sample_count = count_it->second;
uint32_t func_samples = current_function_ptr->sample_count;
double line_percentage = (func_samples > 0) ? (sample_count * 100.0) / func_samples : 0.0;
annotated_file << original_line << " !!!! " << sample_count << " " << std::fixed << std::setprecision(2) << line_percentage << "%" << std::endl;
} else {
// No samples for this line
annotated_file << original_line << std::endl;
}
} else {
// Not within a significant function
annotated_file << original_line << std::endl;
}
}
source_file.close();
annotated_file.close();
std::cout << "[*] Source file annotation completed. Annotated file: " << output_filename << std::endl;
// Generate top functions report
std::cout << "[*] Generating top functions report..." << std::endl;
// Sort functions by sample count in descending order
std::sort(functions.begin(), functions.end(), [](const FunctionInfo& a, const FunctionInfo& b) {
return a.sample_count > b.sample_count;
});
std::cout << "\nTop functions by sample count:" << std::endl;
std::cout << std::setw(6) << "Rank"
<< std::setw(10) << "Samples"
<< std::setw(10) << "Percent"
<< std::setw(10) << "CumSum"
<< " Function Name" << std::endl;
std::cout << "----------------------------------------------------------------" << std::endl;
double cumulative_percentage = 0.0;
for (size_t i = 0; i < functions.size(); ++i) {
double percent = functions[i].sample_percentage;
cumulative_percentage += percent;
// Prioritize demangled name; if empty, use mangled name; if empty, use address
std::string function_label;
if (!functions[i].demangled_name.empty() && functions[i].demangled_name != functions[i].mangled_name) {
function_label = functions[i].demangled_name;
} else if (!functions[i].mangled_name.empty()) {
function_label = functions[i].mangled_name;
} else {
std::ostringstream oss;
oss << "0x" << std::hex << functions[i].start_address;
function_label = oss.str();
}
std::cout << std::setw(6) << (i + 1)
<< std::setw(10) << functions[i].sample_count
<< std::setw(9) << std::fixed << std::setprecision(2) << percent << "%"
<< std::setw(9) << std::fixed << std::setprecision(2) << cumulative_percentage << "%"
<< " " << function_label << std::endl;
#if defined(VERBOSE_CALLERS)
for (auto &caller : functions[i].callers) {
auto func = find_function_by_address(caller.first);
std::cout << " Called by 0x" << std::hex << caller.first << " " << std::dec << caller.second << " times, " << (func ? func->demangled_name : "<unknown>") << std::endl;
}
#endif
}
std::cout << "[*] Top functions report generated." << std::endl;
std::cout << "[*] Profiling data analysis completed." << std::endl;
return 0;
}

585
dreamcast/aud2adpcm.c Normal file
View File

@ -0,0 +1,585 @@
/*
aica adpcm <-> wave converter;
(c) 2002 BERO <bero@geocities.co.jp>
under GPL or notify me
aica adpcm seems same as YMZ280B adpcm
adpcm->pcm algorithm can found MAME/src/sound/ymz280b.c by Aaron Giles
this code is for little endian machine
Modified by Megan Potter to read/write ADPCM WAV files, and to
handle stereo (though the stereo is very likely KOS specific
since we make no effort to interleave it). Please see README.GPL
in the KOS docs dir for more info on the GPL license.
Modified by skmp for dca3
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#define MINIMP3_IMPLEMENTATION
#define MINIMP3_ONLY_MP3
#include "minimp3_ex.h"
static int ym_diff_lookup[16] = {
1, 3, 5, 7, 9, 11, 13, 15,
-1, -3, -5, -7, -9, -11, -13, -15,
};
static int ym_index_scale[16] = {
0x0e6, 0x0e6, 0x0e6, 0x0e6, 0x133, 0x199, 0x200, 0x266,
0x0e6, 0x0e6, 0x0e6, 0x0e6, 0x133, 0x199, 0x200, 0x266 /* same value for speedup */
};
static inline int limit(int val, int min, int max) {
if(val < min) return min;
else if(val > max) return max;
else return val;
}
const int ima_step_size_table[89] = {
7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31,
34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143,
157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658,
724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
};
const int ima_index_table[16] = {
-1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8
};
typedef struct {
int predictor;
int step_index;
} ImaAdpcmDecoder;
void ima_initialize_decoder(ImaAdpcmDecoder *decoder, int initial_predictor, int initial_step_index) {
decoder->predictor = initial_predictor;
decoder->step_index = initial_step_index;
}
int ima_decode_nibble(ImaAdpcmDecoder *decoder, uint8_t nibble) {
int step = ima_step_size_table[decoder->step_index];
int diff = step >> 3;
if (nibble & 1) diff += step >> 2;
if (nibble & 2) diff += step >> 1;
if (nibble & 4) diff += step;
if (nibble & 8)
decoder->predictor -= diff;
else
decoder->predictor += diff;
// Clamp the predictor to 16-bit signed values
if (decoder->predictor > 32767)
decoder->predictor = 32767;
else if (decoder->predictor < -32768)
decoder->predictor = -32768;
// Update step_index
decoder->step_index += ima_index_table[nibble];
if (decoder->step_index < 0) decoder->step_index = 0;
if (decoder->step_index > 88) decoder->step_index = 88;
return decoder->predictor;
}
void ima_decode_adpcm_blocks(const uint8_t *input, int input_size, int16_t *output, size_t output_size, int channels, int block_size) {
ImaAdpcmDecoder decoders[2]; // Support for stereo
int out_index = 0;
for (int i = 0; i + block_size <= input_size; i += block_size) {
// Read block header
int16_t predictor_left = (input[i + 1] << 8) | input[i]; // Predictor for left channel (first 2 bytes)
int step_index_left = input[i + 2]; // Step index for left channel (3rd byte)
output[out_index++] = predictor_left;
int16_t predictor_right = 0;
int step_index_right = 0;
if (channels == 2) {
predictor_right = (input[i + 5] << 8) | input[i + 4]; // Predictor for right channel (4th and 5th bytes)
step_index_right = input[i + 6]; // Step index for right channel (6th byte)
output[out_index++] = predictor_right;
}
// Initialize decoders with block header values
ima_initialize_decoder(&decoders[0], predictor_left, step_index_left);
if (channels == 2) ima_initialize_decoder(&decoders[1], predictor_right, step_index_right);
// Decode nibbles after the header
int start_offset = channels == 2 ? 8 : 4; // Stereo uses 8 bytes for the header, mono uses 4
if (channels == 2) {
for (int j = i + start_offset; j < i + block_size; j+= 8) {
for(int k=j; k<j+4; k++) {
output[out_index++] = ima_decode_nibble(&decoders[0], input[k + 0] & 0x0F);
output[out_index++] = ima_decode_nibble(&decoders[1], input[k + 4] & 0x0F);
if (output_size*2 == out_index) return;
output[out_index++] = ima_decode_nibble(&decoders[0], input[k + 0] >> 4);
output[out_index++] = ima_decode_nibble(&decoders[1], input[k + 4] >> 4);
if (output_size*2 == out_index) return;
}
}
} else {
for (int j = i + start_offset; j < i + block_size; j++) {
uint8_t byte = input[j];
// Mono: Decode both nibbles for the single channel
output[out_index++] = ima_decode_nibble(&decoders[0], byte & 0x0F); // Low nibble
output[out_index++] = ima_decode_nibble(&decoders[0], byte >> 4); // High nibble
if (output_size*2 == out_index) return;
}
}
}
}
#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
static inline int16_t ymz_step(uint8_t step, int16_t *history, int16_t *step_size) {
static const int step_table[8] = {
230, 230, 230, 230, 307, 409, 512, 614
};
int sign = step & 8;
int delta = step & 7;
int diff = ((1 + (delta << 1)) * *step_size) >> 3;
int newval = *history;
int nstep = (step_table[delta] * *step_size) >> 8;
/* Only found in the official AICA encoder
but it's possible all chips (including ADPCM-B) does this. */
diff = CLAMP(diff, 0, 32767);
if(sign > 0)
newval -= diff;
else
newval += diff;
*step_size = CLAMP(nstep, 127, 24576);
*history = newval = CLAMP(newval, -32768, 32767);
return newval;
}
void pcm2adpcm(uint8_t *outbuffer, int16_t *buffer, size_t bytes) {
long i;
int16_t step_size = 127;
int16_t history = 0;
uint8_t buf_sample = 0, nibble = 0;
uint32_t adpcm_sample;
size_t num_samples = bytes / 2; /* Divide by 2 to get the number of 16-bit samples */
for(i = 0;i < num_samples;i++) {
/* We remove a few bits_per_sample of accuracy to reduce some noise. */
int step = ((*buffer++) & -8) - history;
adpcm_sample = (abs(step) << 16) / (step_size << 14);
adpcm_sample = CLAMP(adpcm_sample, 0, 7);
if(step < 0)
adpcm_sample |= 8;
if(!nibble)
*outbuffer++ = buf_sample | (adpcm_sample<<4);
else
buf_sample = (adpcm_sample & 15);
nibble ^= 1;
ymz_step(adpcm_sample, &history, &step_size);
}
}
size_t deinterleave(void *buffer, size_t size) {
short *buf, *buf1, *buf2;
int i;
buf = (short *)buffer;
size_t channel_size = ((size/2) + 8191) & ~8191;
buf1 = calloc(1, channel_size);
buf2 = calloc(1, channel_size);
for (i = 0; i < size / 4; i++) {
buf1[i] = buf[i * 2 + 0];
buf2[i] = buf[i * 2 + 1];
}
memcpy(buf, buf1, channel_size);
memcpy(buf + channel_size / sizeof(*buf), buf2, channel_size);
free(buf1);
free(buf2);
return channel_size;
}
// interleaves every 8KB (16K samples)
size_t interleave_adpcm(void *buffer, size_t bytes) {
uint8_t *buf, *buf1, *buf2;
int i;
buf = malloc(bytes + 64 * 1024);
buf1 = (uint8_t *)buffer;
buf2 = buf1 + bytes / 2;
uint8_t *ptr = buf;
size_t remaining_bytes = bytes/2;
size_t total_bytes = 0;
while (remaining_bytes) {
size_t bytes_to_copy = remaining_bytes > 8 * 1024 ? 8 * 1024 : remaining_bytes;
memset(ptr, 0, 8 * 1024);
memcpy(ptr, buf1, bytes_to_copy);
ptr += 8 * 1024;
buf1 += bytes_to_copy;
memset(ptr, 0, 8 * 1024);
memcpy(ptr, buf2, bytes_to_copy);
ptr += 8 * 1024;
buf2 += bytes_to_copy;
remaining_bytes -= bytes_to_copy;
total_bytes += 16 * 1024;
}
assert(total_bytes <= bytes + 64 * 1024);
memcpy(buffer, buf, total_bytes);
free(buf);
return total_bytes;
}
typedef struct wavhdr_t {
char hdr1[4];
int32_t totalsize;
char hdr2[8];
int32_t hdrsize;
short format;
short channels;
int32_t freq;
int32_t byte_per_sec;
short blocksize;
short bits;
} wavhdr_t;
typedef struct wavhdr3_t {
char hdr3[4];
int32_t datasize;
} wavhdr3_t;
int validate_wav_header(wavhdr_t *wavhdr, wavhdr3_t *wavhdr3, int format, int bits, FILE *in) {
int result = 0;
if (memcmp(wavhdr->hdr1, "RIFF", 4)) {
fprintf(stderr, "Invalid RIFF header.\n");
result = -1;
}
if (memcmp(wavhdr->hdr2, "WAVEfmt ", 8)) {
fprintf(stderr, "Invalid WAVEfmt header.\n");
result = -1;
}
if (wavhdr->hdrsize < 0x10) {
fprintf(stderr, "Invalid header size, %d bytes\n", wavhdr->hdrsize);
result = -1;
} else if (wavhdr->hdrsize > 0x10) {
fprintf(stderr, "Unusual header size, seeking %d bytes\n", wavhdr->hdrsize - 0x10);
fseek(in, wavhdr->hdrsize - 0x10, SEEK_CUR);
}
if (wavhdr->format != format) {
fprintf(stderr, "Unsupported format.\n");
result = -1;
}
if (wavhdr->channels != 1 && wavhdr->channels != 2) {
fprintf(stderr, "Unsupported number of channels.\n");
result = -1;
}
if (wavhdr->bits != bits) {
fprintf(stderr, "Unsupported bit depth.\n");
result = -1;
}
for (;;) {
if (fread(wavhdr3->hdr3, 1, 4, in) != 4) {
fprintf(stderr, "Failed to read next chunk header!\n");
result = -1;
break;
}
if (fread(&wavhdr3->datasize, 1, 4, in) != 4) {
fprintf(stderr, "Failed to read chunk size!\n");
result = -1;
break;
}
if (memcmp(wavhdr3->hdr3, "data", 4)) {
fseek(in, wavhdr3->datasize, SEEK_CUR);
} else {
break;
}
}
return result;
}
int loadMp3(const char *infile, size_t *pcmsize, short **pcmbuf, int *channels, int *freq) {
static mp3dec_t mp3d;
mp3dec_file_info_t info;
if (mp3dec_load(&mp3d, infile, &info, NULL, NULL) || info.samples == 0) {
printf("Error: mp3dec_load() failed\n");
return 0;
} else {
printf("MP3 channels: %d, hz: %d, samples: %ld\n", info.channels, info.hz, info.samples);
*pcmsize = info.samples * sizeof(short);
*pcmbuf = info.buffer;
*channels = info.channels;
*freq = info.hz;
return 1;
}
}
int loadWav(const char *infile, size_t *pcmsize, short **pcmbuf, int *channels, int *freq) {
wavhdr_t wavhdr;
wavhdr3_t wavhdr3;
FILE *in = fopen(infile, "rb");
if (!in) {
printf("can't open %s\n", infile);
return 0;
}
if (fread(&wavhdr, sizeof(wavhdr), 1, in) != 1) {
fprintf(stderr, "Cannot read header.\n");
fclose(in);
return 0;
}
if (validate_wav_header(&wavhdr, &wavhdr3, 1, 16, in)) {
fclose(in);
return 0;
}
*pcmsize = wavhdr3.datasize;
*pcmbuf = malloc(*pcmsize);
if (fread(*pcmbuf, *pcmsize, 1, in) != 1) {
fprintf(stderr, "Cannot read data.\n");
fclose(in);
return 0;
}
printf("PCM: channels: %d, hz: %d, samples: %d\n", wavhdr.channels, wavhdr.freq, wavhdr3.datasize);
*channels = wavhdr.channels;
*freq = wavhdr.freq;
fclose(in);
return 1;
}
int loadWavIMA(const char *infile, size_t *pcmsize, short **pcmbuf, int *channels, int *freq) {
wavhdr_t wavhdr;
wavhdr3_t wavhdr3;
FILE *in = fopen(infile, "rb");
if (!in) {
printf("can't open %s\n", infile);
return 0;
}
if (fread(&wavhdr, sizeof(wavhdr), 1, in) != 1) {
fprintf(stderr, "Cannot read header.\n");
fclose(in);
return 0;
}
if (validate_wav_header(&wavhdr, &wavhdr3, 0x11, 4, in)) {
fclose(in);
return 0;
}
uint8_t *adpcmbuf = malloc(wavhdr3.datasize);
if (fread(adpcmbuf, wavhdr3.datasize, 1, in) != 1) {
fprintf(stderr, "Cannot read data.\n");
free(adpcmbuf);
fclose(in);
return 0;
}
printf("IMA ADPCM: channels: %d, hz: %d, block size: %d, data size: %d\n",
wavhdr.channels, wavhdr.freq, wavhdr.blocksize, wavhdr3.datasize);
// Calculate the number of samples
int samples_per_block = (wavhdr.blocksize - 4 * wavhdr.channels) * 2 / wavhdr.channels + 1;
int total_blocks = wavhdr3.datasize / wavhdr.blocksize;
*pcmsize = total_blocks * samples_per_block * wavhdr.channels * sizeof(short);
*pcmbuf = malloc(*pcmsize);
*channels = wavhdr.channels;
*freq = wavhdr.freq;
ima_decode_adpcm_blocks(adpcmbuf, wavhdr3.datasize, *pcmbuf, *pcmsize, wavhdr.channels, wavhdr.blocksize);
free(adpcmbuf);
fclose(in);
return 1;
}
int aud2adpcm(const char *infile, const char *outfile, int use_hdr, int to_mono, int lq) {
FILE *in, *out;
size_t pcmsize;
short *pcmbuf;
int channels, freq;
if (!loadWav(infile, &pcmsize, &pcmbuf, &channels, &freq) &&
!loadMp3(infile, &pcmsize, &pcmbuf, &channels, &freq) &&
!loadWavIMA(infile, &pcmsize, &pcmbuf, &channels, &freq)) {
fprintf(stderr, "Cannot load input file as wav, mp3, or IMA ADPCM.\n");
return -1;
}
if (to_mono && channels == 2) {
pcmsize /= 2;
for (int i = 0; i < pcmsize / 2; i++) {
pcmbuf[i] = (pcmbuf[i * 2 + 0] + pcmbuf[i * 2 + 1]) / 2;
}
channels = 1;
}
if (lq) {
// Downsample to quarter sample rate
freq /= 4;
short *newbuf = malloc(pcmsize / 4);
if (channels == 1) {
for (int i = 0; i < pcmsize / 8; i++) {
newbuf[i] = (pcmbuf[i * 4 + 0] + pcmbuf[i * 4 + 1] + pcmbuf[i * 4 + 2] + pcmbuf[i * 4 + 3]) / 4;
}
} else {
assert(channels == 2);
for (int i = 0; i < pcmsize / 16; i++) {
newbuf[i * 2 + 0 ] = (pcmbuf[i * 8 + 0] + pcmbuf[i * 8 + 2] + pcmbuf[i * 8 + 4] + pcmbuf[i * 8 + 6]) / 4;
newbuf[i * 2 + 1 ] = (pcmbuf[i * 8 + 1] + pcmbuf[i * 8 + 3] + pcmbuf[i * 8 + 5] + pcmbuf[i * 8 + 7]) / 4;
}
}
memcpy(pcmbuf, newbuf, pcmsize / 4);
free(newbuf);
pcmsize /= 4;
}
// Round down to the nearest multiple of 4
// adpcm data is always a multiple of 4 samples
pcmsize = pcmsize & ~3;
size_t adpcmsize = pcmsize / 4;
size_t adpcmsize_data = adpcmsize;
unsigned char *adpcmbuf = calloc(1, adpcmsize + 64 * 1024);// extra alloc for interleave_adpcm
if (channels == 1) {
pcmbuf = realloc(pcmbuf, pcmsize + 8192);
memset(pcmbuf + pcmsize / sizeof(*pcmbuf), 0, 8192);
pcm2adpcm(adpcmbuf, pcmbuf, pcmsize + 8192/sizeof(*pcmbuf));
if (use_hdr) {
adpcmsize_data = (adpcmsize_data + 8191) & ~8191;
}
} else {
assert(use_hdr == 1);
pcmbuf = realloc(pcmbuf, pcmsize + 8192*2);
size_t channel_size = deinterleave(pcmbuf, pcmsize);
pcm2adpcm(adpcmbuf, pcmbuf, channel_size);
pcm2adpcm(adpcmbuf + channel_size / 4, pcmbuf + channel_size / sizeof(*pcmbuf), channel_size);
adpcmsize_data = interleave_adpcm(adpcmbuf, channel_size/2);
}
out = fopen(outfile, "wb");
if (!out) {
fprintf(stderr, "Cannot write ADPCM data.\n");
fclose(out);
free(pcmbuf);
free(adpcmbuf);
return -1;
}
if (use_hdr) {
wavhdr_t wavhdr;
wavhdr3_t wavhdr3;
memset(&wavhdr, 0, sizeof(wavhdr));
memcpy(wavhdr.hdr1, "RIFF", 4);
wavhdr.totalsize = adpcmsize + sizeof(wavhdr) + sizeof(wavhdr3) - 8;
memcpy(wavhdr.hdr2, "WAVEfmt ", 8);
wavhdr.hdrsize = 0x10;
wavhdr.format = 0x20; /* ITU G.723 ADPCM (Yamaha) */
wavhdr.channels = channels;
wavhdr.freq = freq;
wavhdr.byte_per_sec = freq * channels / 2;
wavhdr.blocksize = 4;
wavhdr.bits = 4;
memset(&wavhdr3, 0, sizeof(wavhdr3));
memcpy(wavhdr3.hdr3, "data", 4);
wavhdr3.datasize = adpcmsize;
fwrite(&wavhdr, sizeof(wavhdr), 1, out);
fwrite(&wavhdr3, sizeof(wavhdr3), 1, out);
// data starts on the second sector
fseek(out, 2048, SEEK_SET);
assert(0 == (adpcmsize_data & 8191));
}
if (fwrite(adpcmbuf, adpcmsize_data, 1, out) != 1) {
fprintf(stderr, "Cannot write ADPCM data.\n");
fclose(out);
free(pcmbuf);
free(adpcmbuf);
return -1;
}
fclose(out);
free(pcmbuf);
free(adpcmbuf);
return 0;
}
void usage() {
printf("based on wav2adpcm: 16bit mono wav to aica adpcm and vice-versa (c)2002 BERO\n"
" wav2adpcm -q <infile.wav/mp3/ima adpcm> <outfile.wav> (To adpcm long stream)\n"
" wav2adpcm -t <infile.wav/mp3/ima adpcm> <outfile.wav> (To adpcm long stream)\n"
" wav2adpcm -raw <infile.wav/mp3/ima adpcm> <outfile.raw> (To adpcm sfx)\n"
"\n"
"If you are having trouble with your input wav file you can run it"
"through ffmpeg first and then run wav2adpcm on output.wav:\n"
" ffmpeg -i input.wav -ac 1 -acodec pcm_s16le output.wav"
);
}
int main(int argc, char **argv) {
if (argc == 4) {
if (!strcmp(argv[1], "-t")) {
return aud2adpcm(argv[2], argv[3], 1, 0, 0);
} else if (!strcmp(argv[1], "-q")) {
return aud2adpcm(argv[2], argv[3], 1, 1, 1);
} else if (!strcmp(argv[1], "-raw")) {
return aud2adpcm(argv[2], argv[3], 0, 0, 0);
} else {
usage();
return -1;
}
} else {
usage();
return -1;
}
}

371
dreamcast/common.mk Normal file
View File

@ -0,0 +1,371 @@
# List all of your C files here, but change the extension to ".o"
# Include "romdisk.o" if you want a rom disk.
RE3_OBJS = \
../src/animation/AnimBlendAssocGroup.o \
../src/animation/AnimBlendAssociation.o \
../src/animation/AnimBlendClumpData.o \
../src/animation/AnimBlendHierarchy.o \
../src/animation/AnimBlendNode.o \
../src/animation/AnimBlendSequence.o \
../src/animation/AnimManager.o \
../src/animation/Bones.o \
../src/animation/CutsceneMgr.o \
../src/animation/FrameUpdate.o \
../src/animation/RpAnimBlend.o \
\
../src/buildings/Building.o \
../src/buildings/Treadable.o \
\
../src/collision/ColBox.o \
../src/collision/ColLine.o \
../src/collision/Collision.o \
../src/collision/ColModel.o \
../src/collision/ColPoint.o \
../src/collision/ColSphere.o \
../src/collision/ColTriangle.o \
../src/collision/TempColModels.o \
../src/collision/VuCollision.o \
\
../src/control/AutoPilot.o \
../src/control/Bridge.o \
../src/control/CarAI.o \
../src/control/CarCtrl.o \
../src/control/Curves.o \
../src/control/Darkel.o \
../src/control/GameLogic.o \
../src/control/Garages.o \
../src/control/NameGrid.o \
../src/control/OnscreenTimer.o \
../src/control/PathFind.o \
../src/control/Phones.o \
../src/control/Pickups.o \
../src/control/PowerPoints.o \
../src/control/Record.o \
../src/control/Remote.o \
../src/control/Replay.o \
../src/control/Restart.o \
../src/control/RoadBlocks.o \
../src/control/SceneEdit.o \
../src/control/Script.o \
../src/control/Script2.o \
../src/control/Script3.o \
../src/control/Script4.o \
../src/control/Script5.o \
../src/control/Script6.o \
../src/control/ScriptDebug.o \
../src/control/TrafficLights.o \
\
../src/core/Accident.o \
../src/core/Cam.o \
../src/core/Camera.o \
../src/core/CdStreamDC.o \
../src/core/Clock.o \
../src/core/ControllerConfig.o \
../src/core/Debug.o \
../src/core/Directory.o \
../src/core/EventList.o \
../src/core/FileLoader.o \
../src/core/FileMgr.o \
../src/core/Fire.o \
../src/core/Frontend.o \
../src/core/FrontEndControls.o \
../src/core/Frontend_PS2.o \
../src/core/Game.o \
../src/core/IniFile.o \
../src/core/Lists.o \
../src/core/main.o \
../src/core/MenuScreens.o \
../src/core/MenuScreensCustom.o \
../src/core/obrstr.o \
../src/core/Pad.o \
../src/core/Placeable.o \
../src/core/PlayerInfo.o \
../src/core/Pools.o \
../src/core/Profile.o \
../src/core/Radar.o \
../src/core/Range2D.o \
../src/core/Range3D.o \
../src/core/re3.o \
../src/core/References.o \
../src/core/Stats.o \
../src/core/Streaming.o \
../src/core/SurfaceTable.o \
../src/core/timebars.o \
../src/core/Timer.o \
../src/core/TimeStep.o \
../src/core/User.o \
../src/core/Wanted.o \
../src/core/World.o \
../src/core/ZoneCull.o \
../src/core/Zones.o \
\
../src/entities/Dummy.o \
../src/entities/Entity.o \
../src/entities/Physical.o \
\
../src/fakerw/fake.o \
\
../src/math/math.o \
../src/math/Matrix.o \
../src/math/Quaternion.o \
../src/math/Rect.o \
../src/math/Vector.o \
\
../src/modelinfo/BaseModelInfo.o \
../src/modelinfo/ClumpModelInfo.o \
../src/modelinfo/MloModelInfo.o \
../src/modelinfo/ModelIndices.o \
../src/modelinfo/ModelInfo.o \
../src/modelinfo/PedModelInfo.o \
../src/modelinfo/SimpleModelInfo.o \
../src/modelinfo/TimeModelInfo.o \
../src/modelinfo/VehicleModelInfo.o \
\
../src/objects/CutsceneHead.o \
../src/objects/CutsceneObject.o \
../src/objects/DummyObject.o \
../src/objects/Object.o \
../src/objects/ObjectData.o \
../src/objects/ParticleObject.o \
../src/objects/Projectile.o \
\
../src/peds/CivilianPed.o \
../src/peds/CopPed.o \
../src/peds/EmergencyPed.o \
../src/peds/Gangs.o \
../src/peds/Ped.o \
../src/peds/PedAI.o \
../src/peds/PedChat.o \
../src/peds/PedDebug.o \
../src/peds/PedFight.o \
../src/peds/PedIK.o \
../src/peds/PedPlacement.o \
../src/peds/PedRoutes.o \
../src/peds/PedType.o \
../src/peds/PlayerPed.o \
../src/peds/Population.o \
\
../src/renderer/Antennas.o \
../src/renderer/Clouds.o \
../src/renderer/Console.o \
../src/renderer/Coronas.o \
../src/renderer/Credits.o \
../src/renderer/Draw.o \
../src/renderer/Fluff.o \
../src/renderer/Font.o \
../src/renderer/Glass.o \
../src/renderer/Hud.o \
../src/renderer/Instance.o \
../src/renderer/Lines.o \
../src/renderer/MBlur.o \
../src/renderer/Particle.o \
../src/renderer/ParticleMgr.o \
../src/renderer/PlayerSkin.o \
../src/renderer/PointLights.o \
../src/renderer/RenderBuffer.o \
../src/renderer/Renderer.o \
../src/renderer/Rubbish.o \
../src/renderer/Shadows.o \
../src/renderer/Skidmarks.o \
../src/renderer/SpecialFX.o \
../src/renderer/Sprite.o \
../src/renderer/Sprite2d.o \
../src/renderer/TexList.o \
../src/renderer/Timecycle.o \
../src/renderer/WaterCannon.o \
../src/renderer/WaterLevel.o \
../src/renderer/Weather.o \
\
../src/rw/ClumpRead.o \
../src/rw/Lights.o \
../src/rw/MemoryHeap.o \
../src/rw/MemoryMgr.o \
../src/rw/NodeName.o \
../src/rw/RwHelper.o \
../src/rw/RwMatFX.o \
../src/rw/RwPS2AlphaTest.o \
../src/rw/TexRead.o \
../src/rw/TexturePools.o \
../src/rw/TxdStore.o \
../src/rw/VisibilityPlugins.o \
\
../src/skel/crossplatform.o \
../src/skel/events.o \
../src/skel/skeleton.o \
../src/skel/dc/dc.o \
\
../src/text/Messages.o \
../src/text/Pager.o \
../src/text/Text.o \
\
../src/vehicles/Automobile.o \
../src/vehicles/Boat.o \
../src/vehicles/CarGen.o \
../src/vehicles/Cranes.o \
../src/vehicles/DamageManager.o \
../src/vehicles/Door.o \
../src/vehicles/Floater.o \
../src/vehicles/HandlingMgr.o \
../src/vehicles/Heli.o \
../src/vehicles/Plane.o \
../src/vehicles/Train.o \
../src/vehicles/Transmission.o \
../src/vehicles/Vehicle.o \
\
../src/weapons/BulletInfo.o \
../src/weapons/Explosion.o \
../src/weapons/ProjectileInfo.o \
../src/weapons/ShotInfo.o \
../src/weapons/Weapon.o \
../src/weapons/WeaponEffects.o \
../src/weapons/WeaponInfo.o \
\
../src/audio/AudioCollision.o \
../src/audio/AudioLogic.o \
../src/audio/AudioManager.o \
../src/audio/AudioScriptObject.o \
../src/audio/DMAudio.o \
../src/audio/MusicManager.o \
../src/audio/PolRadio.o \
../src/audio/sampman_miles.o \
../src/audio/sampman_oal.o \
\
../src/save/Date.o \
../src/save/GenericGameStorage.o \
../src/save/MemoryCard.o \
../src/save/PCSave.o \
\
../src/extras/debugmenu.o \
../src/extras/frontendoption.o \
../src/extras/postfx.o \
../src/extras/screendroplets.o \
\
../src/vmu/vmu.o \
../vendor/miniLZO/minilzo.o \
\
# Excluded \
../src/extras/custompipes.o \
../src/extras/custompipes_d3d9.o \
../src/extras/custompipes_gl.o \
../src/core/CdStream.o \
../src/core/CdStreamPosix.o \
../src/extras \
../src/extras/GitSHA1.cpp.in \
../src/core/AnimViewer.o \
RW_OBJS = \
../vendor/librw/src/anim.o \
../vendor/librw/src/base.o \
../vendor/librw/src/camera.o \
../vendor/librw/src/charset.o \
../vendor/librw/src/clump.o \
../vendor/librw/src/engine.o \
../vendor/librw/src/error.o \
../vendor/librw/src/frame.o \
../vendor/librw/src/geometry.o \
../vendor/librw/src/geoplg.o \
../vendor/librw/src/hanim.o \
../vendor/librw/src/image.o \
../vendor/librw/src/light.o \
../vendor/librw/src/matfx.o \
../vendor/librw/src/pipeline.o \
../vendor/librw/src/plg.o \
../vendor/librw/src/prim.o \
../vendor/librw/src/raster.o \
../vendor/librw/src/render.o \
../vendor/librw/src/skin.o \
../vendor/librw/src/texture.o \
../vendor/librw/src/tristrip.o \
../vendor/librw/src/userdata.o \
../vendor/librw/src/uvanim.o \
../vendor/librw/src/world.o \
\
../vendor/librw/src/dc/rwdc.o \
../vendor/librw/src/dc/alloc.o
# Excluded \
../vendor/librw/src/d3d-x/d3d.o \
../vendor/librw/src/d3d-x/d3d8.o \
../vendor/librw/src/d3d-x/d3d8render.o \
../vendor/librw/src/d3d/d3d8.o \
../vendor/librw/src/d3d/d3d8matfx.o \
../vendor/librw/src/d3d/d3d8render.o \
../vendor/librw/src/d3d/d3d8skin.o \
../vendor/librw/src/d3d/d3d9.o \
../vendor/librw/src/d3d/d3d9matfx.o \
../vendor/librw/src/d3d/d3d9render.o \
../vendor/librw/src/d3d/d3d9skin.o \
../vendor/librw/src/d3d/d3d.o \
../vendor/librw/src/d3d/d3ddevice.o \
../vendor/librw/src/d3d/d3dimmed.o \
../vendor/librw/src/d3d/d3drender.o \
../vendor/librw/src/d3d/xbox.o \
../vendor/librw/src/d3d/xboxmatfx.o \
../vendor/librw/src/d3d/xboxskin.o \
../vendor/librw/src/d3d/xboxvfmt.o \
\
../vendor/librw/src/gl/gl3.o \
../vendor/librw/src/gl/gl3device.o \
../vendor/librw/src/gl/gl3immed.o \
../vendor/librw/src/gl/gl3matfx.o \
../vendor/librw/src/gl/gl3pipe.o \
../vendor/librw/src/gl/gl3raster.o \
../vendor/librw/src/gl/gl3render.o \
../vendor/librw/src/gl/gl3shader.o \
../vendor/librw/src/gl/gl3skin.o \
../vendor/librw/src/gl/wdgl.o \
../vendor/librw/src/gl/glad/glad.cXXX \
\
../vendor/librw/src/ps2/pds.o \
../vendor/librw/src/ps2/ps2.o \
../vendor/librw/src/ps2/ps2device.o \
../vendor/librw/src/ps2/ps2matfx.o \
../vendor/librw/src/ps2/ps2raster.o \
../vendor/librw/src/ps2/ps2skin.o \
INCLUDE = \
-I../src/animation \
-I../src/audio \
-I../src/buildings \
-I../src/collision \
-I../src/control \
-I../src/core \
-I../src/entities \
-I../src/extras \
-I../src/fakerw \
-I../src/math \
-I../src/modelinfo \
-I../src/objects \
-I../src/peds \
-I../src/renderer \
-I../src/rw \
-I../src/save \
-I../src/skel \
-I../src/text \
-I../src/vehicles \
-I../src/weapons \
-I../src/audio/eax \
-I../src/audio/oal \
-I../src/extras/shaders \
-I../src/extras/shaders/obj \
-I../src/skel/glfw \
-I../src/skel/win \
\
-I../vendor/librw \
\
-I../vendor/miniLZO
DEFINES = -DRW_DC -DLIBRW $(if $(WITH_LOGGING),-DWITH_LOGGING) $(if $(WITH_DCLOAD),-DDC_CHDIR=/pc) \
$(if $(WITH_BEEPS),-DWITH_BEEPS)
FLAGS = -fpermissive -Wno-sign-compare -Wno-parentheses -Wno-maybe-uninitialized \
-Wno-format -Wno-strict-aliasing -Wno-unused-variable \
-Wno-unused-but-set-variable -Wno-write-strings \
-Wno-deprecated-enum-enum-conversion -Wno-deprecated-enum-float-conversion \
-Wno-multichar -Wno-unused-value -Wno-char-subscripts -Wno-reorder \
-Wno-unused-function -Wno-class-memaccess -fno-permissive
CPPFLAGS += $(INCLUDE) $(DEFINES) $(FLAGS)
CFLAGS += -std=gnu17 $(CPPFLAGS)
CXXFLAGS += -std=gnu++20 $(CPPFLAGS)

140
dreamcast/extract-sfx.cpp Normal file
View File

@ -0,0 +1,140 @@
#include <iostream>
#include <fstream>
#include <cstdint>
#include <vector>
#include <cstring>
#include <cassert>
// Define the tSample structure
struct tSample {
uint32_t nOffset;
uint32_t nSize;
uint32_t nFrequency;
uint32_t nLoopStart;
int32_t nLoopEnd;
};
// WAV file header structure
struct WavHeader {
char riff[4]; // "RIFF"
uint32_t fileSize; // File size minus 8 bytes
char wave[4]; // "WAVE"
char fmt[4]; // "fmt "
uint32_t fmtSize; // Size of fmt chunk (16 for PCM)
uint16_t audioFormat; // Audio format (1 for PCM)
uint16_t numChannels; // Number of channels
uint32_t sampleRate; // Sample rate
uint32_t byteRate; // Byte rate
uint16_t blockAlign; // Block align
uint16_t bitsPerSample; // Bits per sample
char data[4]; // "data"
uint32_t dataSize; // Size of data chunk
};
void writeWavFile(const std::string &filename, const std::vector<int16_t> &data, uint32_t sampleRate) {
WavHeader header;
// Fill in the WAV header fields
std::memcpy(header.riff, "RIFF", 4);
header.fileSize = 36 + data.size() * sizeof(int16_t);
std::memcpy(header.wave, "WAVE", 4);
std::memcpy(header.fmt, "fmt ", 4);
header.fmtSize = 16;
header.audioFormat = 1; // PCM
header.numChannels = 1; // Mono
header.sampleRate = sampleRate;
header.byteRate = sampleRate * sizeof(int16_t);
header.blockAlign = sizeof(int16_t);
header.bitsPerSample = 16;
std::memcpy(header.data, "data", 4);
header.dataSize = data.size() * sizeof(int16_t);
// Open the output WAV file
std::ofstream outFile(filename, std::ios::binary);
if (!outFile) {
std::cerr << "Failed to open output file: " << filename << std::endl;
return;
}
// Write the header
outFile.write(reinterpret_cast<char*>(&header), sizeof(WavHeader));
// Write the audio data
outFile.write(reinterpret_cast<const char*>(data.data()), data.size() * sizeof(int16_t));
outFile.close();
}
int main(int argc, char *argv[]) {
// Check if enough arguments are provided
if (argc != 4) {
std::cerr << "Usage: " << argv[0] << " <sample_file> <data_file> <out_folder>" << std::endl;
return 1;
}
// Open the sample file in binary mode
std::ifstream sampleFile(argv[1], std::ios::binary);
if (!sampleFile) {
std::cerr << "Failed to open sample file: " << argv[1] << std::endl;
return 1;
}
// Open the data file in binary mode
std::ifstream dataFile(argv[2], std::ios::binary);
if (!dataFile) {
std::cerr << "Failed to open data file: " << argv[2] << std::endl;
return 1;
}
std::string folder = argv[3];
// Read and process all tSample structures from the sample file
tSample sample;
int recordNumber = 0;
while (sampleFile.read(reinterpret_cast<char*>(&sample), sizeof(tSample))) {
// Seek to the correct offset in the data file
dataFile.seekg(sample.nOffset, std::ios::beg);
if (!dataFile) {
std::cerr << "Failed to seek in data file for record " << recordNumber << std::endl;
continue;
}
assert(sample.nSize % sizeof(int16_t) == 0);
assert(sample.nLoopStart % sizeof(int16_t) == 0);
assert(sample.nLoopEnd == -1 || sample.nLoopEnd % sizeof(int16_t) == 0);
// Read the sample data
std::vector<int16_t> audioData(sample.nSize / sizeof(int16_t));
if (!dataFile.read(reinterpret_cast<char*>(audioData.data()), sample.nSize)) {
std::cerr << "Failed to read data for record " << recordNumber << std::endl;
continue;
}
// Generate the WAV file name
std::string wavFilename = folder + "/sfx_" + std::to_string(recordNumber) + ".wav";
std::string wavFilenameLoop = folder + "/sfx_" + std::to_string(recordNumber) + "_loop.wav";
// std::cout << "Sample size: " << sample.nSize << " bytes" << std::endl;
// std::cout << "Sample frequency: " << sample.nFrequency << " Hz" << std::endl;
// std::cout << "Sample loop start: " << sample.nLoopStart << std::endl;
// std::cout << "Sample loop end: " << sample.nLoopEnd << std::endl;
// std::cout << std::endl;
// Write the data to a WAV file
if (sample.nLoopStart != 0) {
writeWavFile(wavFilename, audioData, sample.nFrequency);
std::vector<int16_t> loopData(audioData.begin() + sample.nLoopStart/2, audioData.begin() + sample.nLoopEnd/2);
writeWavFile(wavFilenameLoop, loopData, sample.nFrequency);
} else {
writeWavFile(wavFilename, audioData, sample.nFrequency);
}
recordNumber++;
}
// Close the files
sampleFile.close();
dataFile.close();
return 0;
}

View File

@ -0,0 +1,114 @@
def variant(small_xyz, pad_xyz, small_uv):
return f"""
&submitMesh<false,0, false, false, false, false, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
&submitMesh<true,0, false, false, false, false, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
&submitMesh<false,0, true, false, false, false, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
&submitMesh<true,0, true, false, false, false, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
&submitMesh<false,0, false, true, false, false, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
&submitMesh<true,0, false, true, false, false, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
&submitMesh<false,0, true, true, false, false, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
&submitMesh<true,0, true, true, false, false, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
0,
0,
0,
0,
&submitMesh<false,0, false, true, true, false, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
&submitMesh<true,0, false, true, true, false, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
&submitMesh<false,0, true, true, true, false, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
&submitMesh<true,0, true, true, true, false, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
&submitMesh<false,0, false, false, false, true, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
&submitMesh<true,0, false, false, false, true, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
&submitMesh<false,0, true, false, false, true, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
&submitMesh<true,0, true, false, false, true, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
&submitMesh<false,0, false, true, false, true, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
&submitMesh<true,0, false, true, false, true, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
&submitMesh<false,0, true, true, false, true, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
&submitMesh<true,0, true, true, false, true, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
0,
0,
0,
0,
&submitMesh<false,0, false, true, true, true, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
&submitMesh<true,0, false, true, true, true, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
&submitMesh<false,0, true, true, true, true, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
&submitMesh<true,0, true, true, true, true, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
0,
&submitMesh<true,1, false, false, false, false, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
0,
&submitMesh<true,1, true, false, false, false, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
0,
&submitMesh<true,1, false, true, false, false, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
0,
&submitMesh<true,1, true, true, false, false, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
0,
0,
0,
0,
0,
&submitMesh<true,1, false, true, true, false, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
0,
&submitMesh<true,1, true, true, true, false, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
0,
&submitMesh<true,1, false, false, false, true, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
0,
&submitMesh<true,1, true, false, false, true, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
0,
&submitMesh<true,1, false, true, false, true, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
0,
&submitMesh<true,1, true, true, false, true, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
0,
0,
0,
0,
0,
&submitMesh<true,1, false, true, true, true, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
0,
&submitMesh<true,1, true, true, true, true, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
0,
&submitMesh<true,2, false, false, false, false, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
0,
&submitMesh<true,2, true, false, false, false, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
0,
&submitMesh<true,2, false, true, false, false, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
0,
&submitMesh<true,2, true, true, false, false, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
0,
0,
0,
0,
0,
&submitMesh<true,2, false, true, true, false, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
0,
&submitMesh<true,2, true, true, true, false, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
0,
&submitMesh<true,2, false, false, false, true, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
0,
&submitMesh<true,2, true, false, false, true, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
0,
&submitMesh<true,2, false, true, false, true, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
0,
&submitMesh<true,2, true, true, false, true, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
0,
0,
0,
0,
0,
&submitMesh<true,2, false, true, true, true, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
0,
&submitMesh<true,2, true, true, true, true, {'true' if small_xyz else 'false'}, {'true' if small_xyz and pad_xyz else 'false'}, {'true' if small_uv else 'false'}>,
"""
str = ""
str += variant(False, False, False)
str += variant(False, False, True)
str += variant(False, True, False)
str += variant(False, True, True)
str += variant(True, False, False)
str += variant(True, False, True)
str += variant(True, True, False)
str += variant(True, True, True)
print(str)

178
dreamcast/gta3files.mk Normal file
View File

@ -0,0 +1,178 @@
MISC_FILES = \
anim/cuts.dir \
anim/cuts.img \
anim/gta3.ini \
anim/ped.ifp \
data/animviewer.dat \
data/carcols.dat \
data/CULLZONE.DAT \
data/default.dat \
data/default.ide \
data/fistfite.dat \
data/gta3.dat \
data/gta3.zon \
data/handling.cfg \
data/main.scm \
data/map.zon \
data/maps/comnbtm/comNbtm.col \
data/maps/comnbtm/comnbtm.ide \
data/maps/comnbtm/comNbtm.ipl \
data/maps/comNbtm.ipl \
data/maps/comntop/comNtop.col \
data/maps/comntop/comntop.ide \
data/maps/comntop/comNtop.ipl \
data/maps/comNtop.ipl \
data/maps/comroad/comroad.col \
data/maps/comroad/comroad.ide \
data/maps/comroad/comroad.ipl \
data/maps/comse/comSE.col \
data/maps/comse/comse.ide \
data/maps/comse/comSE.ipl \
data/maps/comSE.ipl \
data/maps/comsw/comSW.col \
data/maps/comsw/comsw.ide \
data/maps/comsw/comSW.ipl \
data/maps/comSW.ipl \
data/maps/cull.ipl \
data/maps/generic.ide \
data/maps/gta3.IDE \
data/maps/indroads/indroads.col \
data/maps/indroads/indroads.ide \
data/maps/indroads/indroads.ipl \
data/maps/industne/industNE.col \
data/maps/industne/industne.ide \
data/maps/industne/industNE.ipl \
data/maps/industNE.ipl \
data/maps/industnw/industNW.col \
data/maps/industnw/industnw.ide \
data/maps/industnw/industNW.ipl \
data/maps/industNW.ipl \
data/maps/industse/industSE.col \
data/maps/industse/industse.ide \
data/maps/industse/industSE.ipl \
data/maps/industSE.ipl \
data/maps/industsw/industSW.col \
data/maps/industsw/industsw.ide \
data/maps/industsw/industSW.ipl \
data/maps/industSW.ipl \
data/maps/landne/landne.col \
data/maps/landne/landne.ide \
data/maps/landne/landne.ipl \
data/maps/landsw/landsw.col \
data/maps/landsw/landsw.ide \
data/maps/landsw/landsw.ipl \
data/maps/making/making.col \
data/maps/making/making.ide \
data/maps/making/making.ipl \
data/maps/overview.ipl \
data/maps/props.IPL \
data/maps/subroads/subroads.col \
data/maps/subroads/subroads.ide \
data/maps/subroads/subroads.ipl \
data/maps/suburbne.ipl \
data/maps/suburbsw.ipl \
data/maps/temppart/temppart.col \
data/maps/temppart/temppart.ide \
data/maps/temppart/temppart.ipl \
data/object.dat \
data/particle.cfg \
data/paths/CHASE0.DAT \
data/paths/CHASE1.DAT \
data/paths/CHASE10.DAT \
data/paths/CHASE11.DAT \
data/paths/CHASE14.DAT \
data/paths/CHASE16.DAT \
data/paths/CHASE18.DAT \
data/paths/CHASE19.DAT \
data/paths/CHASE2.DAT \
data/paths/CHASE3.DAT \
data/paths/CHASE4.DAT \
data/paths/CHASE5.DAT \
data/paths/CHASE6.DAT \
data/paths/CHASE7.DAT \
data/paths/flight.dat \
data/paths/flight2.dat \
data/paths/flight3.dat \
data/paths/flight4.dat \
data/paths/tracks.dat \
data/paths/tracks2.dat \
data/ped.dat \
data/pedgrp.dat \
data/pedstats.dat \
data/surface.dat \
data/timecyc.dat \
data/train.dat \
data/train2.dat \
data/water.dat \
data/waterpro.dat \
data/weapon.dat \
gta3.ini \
models/Coll/commer.col \
models/Coll/generic.col \
models/Coll/indust.col \
models/Coll/peds.col \
models/Coll/suburb.col \
models/Coll/vehicles.col \
models/Coll/weapons.col \
models/Generic/air_vlo.DFF \
models/Generic/arrow.DFF \
models/Generic/loplyguy.dff \
models/Generic/peds.dff \
models/Generic/qsphere.DFF \
models/Generic/sphere.DFF \
models/Generic/weapons.dff \
models/Generic/wheels.DFF \
models/Generic/zonecyla.DFF \
models/Generic/zonecylb.DFF \
models/Generic/zonesphr.DFF \
movies/GTAtitles.mpg \
movies/GTAtitlesGER.mpg \
$(if $(wildcard $(GTA_DIR)/movies/LOGO.mpg), movies/LOGO.mpg, movies/Logo.mpg) \
TEXT/american.gxt \
TEXT/english.gxt \
TEXT/french.gxt \
TEXT/german.gxt \
TEXT/italian.gxt \
TEXT/spanish.gxt \
\
models/fonts.txd \
models/frontend.txd \
models/generic.txd \
models/hud.txd \
models/menu.txd \
models/MISC.TXD \
models/particle.txd \
txd/LOADSC0.TXD \
txd/LOADSC1.TXD \
txd/LOADSC10.TXD \
txd/LOADSC11.TXD \
txd/LOADSC12.TXD \
txd/LOADSC13.TXD \
txd/LOADSC14.TXD \
txd/LOADSC15.TXD \
txd/LOADSC16.TXD \
txd/LOADSC17.TXD \
txd/LOADSC18.TXD \
txd/LOADSC19.TXD \
txd/LOADSC2.TXD \
txd/LOADSC20.TXD \
txd/LOADSC21.TXD \
txd/LOADSC22.TXD \
txd/LOADSC23.TXD \
txd/LOADSC24.TXD \
txd/LOADSC25.TXD \
txd/LOADSC3.TXD \
txd/LOADSC4.TXD \
txd/LOADSC5.TXD \
txd/LOADSC6.TXD \
txd/LOADSC7.TXD \
txd/LOADSC8.TXD \
txd/LOADSC9.TXD \
txd/mainsc1.txd \
txd/mainsc2.txd \
txd/NEWS.TXD \
txd/SPLASH1.TXD \
txd/SPLASH2.TXD \
txd/SPLASH3.TXD \
\
audio/sfx.SDT

147
dreamcast/imgtool.cpp Normal file
View File

@ -0,0 +1,147 @@
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <filesystem>
#include <cstring>
#include <iomanip>
// Sector size constant
const size_t SECTOR_SIZE = 2048;
// Record structure for .dir file
struct DirRecord {
uint32_t offset;
uint32_t size;
char name[24];
};
// Function to read .dir file
std::vector<DirRecord> readDirFile(const std::string& dirFilePath) {
std::ifstream dirFile(dirFilePath, std::ios::binary);
if (!dirFile) {
throw std::runtime_error("Could not open .dir file.");
}
std::vector<DirRecord> records;
DirRecord record;
while (dirFile.read(reinterpret_cast<char*>(&record), sizeof(DirRecord))) {
records.push_back(record);
}
return records;
}
// Function to unpack .img file
void unpackImg(const std::string& baseName, const std::string& dataDir) {
std::string dirFilePath = baseName + ".dir";
std::string imgFilePath = baseName + ".img";
auto records = readDirFile(dirFilePath);
std::ifstream imgFile(imgFilePath, std::ios::binary);
if (!imgFile) {
throw std::runtime_error("Could not open .img file.");
}
std::filesystem::create_directories(dataDir);
for (const auto& record : records) {
std::string outputFilePath = dataDir + "/" + std::string(record.name);
std::ofstream outputFile(outputFilePath, std::ios::binary);
if (!outputFile) {
throw std::runtime_error("Could not create output file: " + outputFilePath);
}
imgFile.seekg(record.offset * SECTOR_SIZE);
std::vector<char> buffer(record.size * SECTOR_SIZE);
imgFile.read(buffer.data(), buffer.size());
outputFile.write(buffer.data(), buffer.size());
}
}
// Function to create .dir file
void writeDirFile(const std::string& dirFilePath, const std::vector<DirRecord>& records) {
std::ofstream dirFile(dirFilePath, std::ios::binary);
if (!dirFile) {
throw std::runtime_error("Could not create .dir file.");
}
for (const auto& record : records) {
dirFile.write(reinterpret_cast<const char*>(&record), sizeof(DirRecord));
}
}
// Function to pack .img file
void packImg(const std::string& dataDir, const std::string& baseName) {
std::string dirFilePath = baseName + ".dir";
std::string imgFilePath = baseName + ".img";
std::ofstream imgFile(imgFilePath, std::ios::binary);
if (!imgFile) {
throw std::runtime_error("Could not create .img file.");
}
std::vector<DirRecord> records;
uint32_t offset = 0;
for (const auto& entry : std::filesystem::directory_iterator(dataDir)) {
if (entry.is_regular_file()) {
DirRecord record;
std::memset(&record, 0, sizeof(DirRecord));
std::strncpy(record.name, entry.path().filename().string().c_str(), sizeof(record.name) - 1);
std::ifstream inputFile(entry.path(), std::ios::binary | std::ios::ate);
if (!inputFile) {
throw std::runtime_error("Could not open input file: " + entry.path().string());
}
size_t fileSize = inputFile.tellg();
inputFile.seekg(0);
size_t numSectors = (fileSize + SECTOR_SIZE - 1) / SECTOR_SIZE;
std::vector<char> buffer(numSectors * SECTOR_SIZE, 0);
inputFile.read(buffer.data(), fileSize);
record.offset = offset;
record.size = numSectors;
imgFile.write(buffer.data(), buffer.size());
records.push_back(record);
offset += numSectors;
}
}
writeDirFile(dirFilePath, records);
}
int main(int argc, char* argv[]) {
if (argc != 4) {
std::cerr << "Usage: imgtool <unpack|pack> <base name> <data dir>" << std::endl;
return 1;
}
std::string command = argv[1];
std::string baseName = argv[2];
std::string dataDir = argv[3];
try {
if (command == "unpack") {
unpackImg(baseName, dataDir);
} else if (command == "pack") {
packImg(dataDir, baseName);
} else {
std::cerr << "Unknown command: " << command << std::endl;
return 1;
}
} catch (const std::exception& ex) {
std::cerr << "Error: " << ex.what() << std::endl;
return 1;
}
return 0;
}

9
dreamcast/ip.txt Normal file
View File

@ -0,0 +1,9 @@
Device Info : CD-ROM1/1
Area Symbols : JUE
Peripherals : E000F10
Product No : T0000
Version : V1.000
Release Date : 20000627
Boot Filename : 1ST_READ.BIN
SW Maker Name : the gang
Game Title : DCA3

3135
dreamcast/modlist.mk Normal file

File diff suppressed because it is too large Load Diff

71
dreamcast/mp3list.mk Normal file
View File

@ -0,0 +1,71 @@
STREAM_MP3 = \
BET.mp3 \
END.mp3 \
JB.mp3 \
c1_tex.mp3 \
d1_stog.mp3 \
d2_kk.mp3 \
d3_ado.mp3 \
d4_gta.mp3 \
d4_gta2.mp3 \
d5_es.mp3 \
d6_sts.mp3 \
d7_mld.mp3 \
el_ph1.mp3 \
el_ph2.mp3 \
el_ph3.mp3 \
el_ph4.mp3 \
hd_ph1.mp3 \
hd_ph2.mp3 \
hd_ph3.mp3 \
hd_ph4.mp3 \
hd_ph5.mp3 \
j0_dm2.mp3 \
j1_lfl.mp3 \
j2_kcl.mp3 \
j3_vh.mp3 \
j4_eth.mp3 \
j5_dst.mp3 \
j6_tbj.mp3 \
k1_kbo.mp3 \
k2_gis.mp3 \
k3_ds.mp3 \
k4_shi.mp3 \
k4_shi2.mp3 \
k5_sd.mp3 \
l1_lg.mp3 \
l2_dsb.mp3 \
l3_dm.mp3 \
l4_pap.mp3 \
l5_tfb.mp3 \
mt_ph1.mp3 \
mt_ph2.mp3 \
mt_ph3.mp3 \
mt_ph4.mp3 \
r0_pdr2.mp3 \
r1_sw.mp3 \
r2_ap.mp3 \
r3_ed.mp3 \
r4_gf.mp3 \
r5_pb.mp3 \
r6_mm.mp3 \
s0_mas.mp3 \
s1_pf.mp3 \
s2_ctg.mp3 \
s2_ctg2.mp3 \
s3_rtc.mp3 \
s4_bdba.mp3 \
s4_bdbb.mp3 \
s4_bdbd.mp3 \
s5_lrq.mp3 \
s5_lrqb.mp3 \
s5_lrqc.mp3 \
t1_tol.mp3 \
t2_tpu.mp3 \
t3_mas.mp3 \
t4_tat.mp3 \
t5_bf.mp3 \
yd_ph1.mp3 \
yd_ph2.mp3 \
yd_ph3.mp3 \
yd_ph4.mp3

224
dreamcast/pack-sfx.cpp Normal file
View File

@ -0,0 +1,224 @@
#include <iostream>
#include <fstream>
#include <vector>
#include <cstring>
#include <cstdint>
#include <filesystem> // C++17
#include <stdexcept>
#ifdef _MSC_VER
#define PACKED_STRUCT __pragma(pack(push,1)) struct __pragma(pack(pop))
#else
#define PACKED_STRUCT struct __attribute__((packed))
#endif
// Structures as specified:
// Matches what the SDT file contains
PACKED_STRUCT tSampleSDT {
uint32_t nOffset; // Not necessarily used for writing new data, but read from SDT
uint32_t nSize; // Not necessarily used for writing new data, but read from SDT
uint32_t nFrequency; // We'll store this into the DSC
uint32_t nLoopStartInBytes; // We'll convert this to "samples" in DSC
int32_t nLoopEnd; // Not used directly in the DSC, but read from SDT
};
// Matches what we want in the DSC file
PACKED_STRUCT tSample {
uint32_t nFileOffset; // in bytes
uint32_t nByteSize; // in bytes
uint32_t nFrequency; // in hz
uint32_t nLoopStartSample; // in samples, 0 if no separate loop data
uint32_t nLoopFileOffset; // in bytes, 0 if none
uint32_t nLoopByteSize; // in bytes, 0 if none
};
static void usage(const char* progName) {
std::cerr << "Usage: " << progName << " <inputSDT> <outputRAW> <outputDSC> <SFX_DIR>\n";
std::cerr << "Example:\n";
std::cerr << " " << progName << " data.sdt merged.raw output.dsc sfx_folder\n";
}
// Helper function to read the entire contents of a binary file into a vector of bytes
static std::vector<char> readFile(const std::string& filename)
{
std::ifstream ifs(filename, std::ios::binary | std::ios::ate);
if (!ifs.is_open()) {
throw std::runtime_error("Could not open file: " + filename);
}
std::streamsize size = ifs.tellg();
ifs.seekg(0, std::ios::beg);
std::vector<char> buffer(size);
if (!ifs.read(buffer.data(), size)) {
throw std::runtime_error("Failed to read file: " + filename);
}
return buffer;
}
// Helper function to write a buffer to a binary file (append mode or from start)
static void writeFileAppend(const std::string& filename, const char* data, size_t size, bool append = true)
{
std::ios_base::openmode mode = std::ios::binary;
if (append) {
mode |= std::ios::app;
} else {
mode |= std::ios::trunc;
}
std::ofstream ofs(filename, mode);
if (!ofs.is_open()) {
throw std::runtime_error("Could not open file for writing: " + filename);
}
ofs.write(data, size);
}
int main(int argc, char** argv)
{
if (argc != 5) {
usage(argv[0]);
return 1;
}
std::string sdtPath = argv[1]; // input SDT file
std::string rawPath = argv[2]; // output RAW file
std::string dscPath = argv[3]; // output DSC file
std::string sfxFolder = argv[4]; // SFX_DIR folder
// Read the entire SDT file into memory
std::vector<char> sdtData;
try {
sdtData = readFile(sdtPath);
} catch(const std::exception& e) {
std::cerr << "Error reading SDT file: " << e.what() << std::endl;
return 1;
}
// Determine how many tSampleSDT entries are in the SDT file
size_t totalBytes = sdtData.size();
if (totalBytes % sizeof(tSampleSDT) != 0) {
std::cerr << "SDT file size is not a multiple of tSampleSDT struct size.\n";
return 1;
}
size_t numSamples = totalBytes / sizeof(tSampleSDT);
// Parse all tSampleSDT entries
std::vector<tSampleSDT> sdtEntries(numSamples);
std::memcpy(sdtEntries.data(), sdtData.data(), totalBytes);
// We'll build DSC descriptors in memory first
std::vector<tSample> dscEntries(numSamples);
// Truncate (or create) the RAW file before we start appending
{
std::ofstream ofs(rawPath, std::ios::binary | std::ios::trunc);
if (!ofs.is_open()) {
std::cerr << "Error creating/truncating RAW file: " << rawPath << std::endl;
return 1;
}
}
// Now process each entry in the SDT, find the corresponding sfx_<n>.pcm / sfx_<n>_loop.pcm
// Concatenate them into the RAW, and fill in the DSC.
uint64_t currentOffset = 0; // Keep track of where we are in the RAW file
for (size_t i = 0; i < numSamples; ++i)
{
tSampleSDT& sdt = sdtEntries[i];
tSample& desc = dscEntries[i];
// Prepare file paths
std::string basePcm = sfxFolder + "/sfx_" + std::to_string(i) + ".pcm";
std::string loopPcm = sfxFolder + "/sfx_" + std::to_string(i) + "_loop.pcm";
// Read main PCM (sfx_<n>.pcm)
uint32_t mainOffset = 0;
uint32_t mainByteSize = 0;
uint32_t loopOffset = 0;
uint32_t loopByteSize = 0;
uint32_t loopStartSamp = 0;
// For the main PCM
try {
if (std::filesystem::exists(basePcm)) {
std::vector<char> buffer = readFile(basePcm);
mainByteSize = static_cast<uint32_t>(buffer.size());
mainOffset = static_cast<uint32_t>(currentOffset);
if (buffer.size() & 3) {
// Pad to 4-byte boundary
size_t padSize = 4 - (buffer.size() & 3);
buffer.insert(buffer.end(), padSize, 0);
// std::cerr << "Warning: Padded main PCM for index " << i << " with " << padSize << " bytes" << std::endl;
}
// Write to RAW
writeFileAppend(rawPath, buffer.data(), buffer.size(), true);
// Advance current offset
currentOffset += buffer.size();
} else {
// If the main PCM doesn't exist, you could decide to throw an error or just keep zero
// For now, let's throw an error
throw std::runtime_error("Missing PCM file: " + basePcm);
}
} catch(const std::exception& e) {
std::cerr << "Error processing main PCM for index " << i << ": " << e.what() << std::endl;
return 1;
}
// For the loop PCM (sfx_<n>_loop.pcm); it might not exist
if (std::filesystem::exists(loopPcm)) {
try {
std::vector<char> bufferLoop = readFile(loopPcm);
loopByteSize = static_cast<uint32_t>(bufferLoop.size());
loopOffset = static_cast<uint32_t>(currentOffset);
if (bufferLoop.size() & 3) {
// Pad to 4-byte boundary
size_t padSize = 4 - (bufferLoop.size() & 3);
bufferLoop.insert(bufferLoop.end(), padSize, 0);
// std::cerr << "Warning: Padded loop PCM for index " << i << " with " << padSize << " bytes." << std::endl;
}
// Write to RAW
writeFileAppend(rawPath, bufferLoop.data(), bufferLoop.size(), true);
// Advance current offset
currentOffset += bufferLoop.size();
} catch(const std::exception& e) {
std::cerr << "Error processing loop PCM for index " << i << ": " << e.what() << std::endl;
return 1;
}
// Convert loopStartInBytes from SDT to samples
// According to the note: "Note each sample in the SDT is indicated by two bytes"
// So if the SDT says 'nLoopStartInBytes', to get the loop start in samples, divide by 2
loopStartSamp = sdt.nLoopStartInBytes / 2;
} else {
// If there's no loop file, we leave loopOffset, loopByteSize, and loopStartSamp = 0
}
// Fill in the tSample descriptor
desc.nFileOffset = mainOffset;
desc.nByteSize = mainByteSize;
desc.nFrequency = sdt.nFrequency;
desc.nLoopStartSample = loopStartSamp;
desc.nLoopFileOffset = loopOffset;
desc.nLoopByteSize = loopByteSize;
}
// Finally, write the DSC file as a binary array of tSample
{
std::ofstream dscOut(dscPath, std::ios::binary | std::ios::trunc);
if (!dscOut.is_open()) {
std::cerr << "Error creating DSC file: " << dscPath << std::endl;
return 1;
}
dscOut.write(reinterpret_cast<const char*>(dscEntries.data()),
dscEntries.size() * sizeof(tSample));
}
std::cout << "Successfully packed " << numSamples << " samples.\n";
std::cout << "Output RAW: " << rawPath << std::endl;
std::cout << "Output DSC: " << dscPath << std::endl;
return 0;
}

3
dreamcast/pvrtex/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.o
pvrtex
pvrtex.exe

42
dreamcast/pvrtex/Makefile Normal file
View File

@ -0,0 +1,42 @@
#Uncomment when debugging
#DEBUGBUILD = true
TARGET = pvrtex
OBJS = elbg.o mem.o log.o bprint.o avstring.o lfg.o crc.o md5.o stb_image_impl.o \
stb_image_write_impl.o stb_image_resize_impl.o optparse_impl.o pvr_texture.o \
dither.o tddither.o vqcompress.o mycommon.o file_common.o \
file_pvr.o file_tex.o file_dctex.o pvr_texture_encoder.o main.o
ifdef $(DEBUGBUILD)
OPTMODE= -Og -pg -g
else
OPTMODE= -O3 -flto
endif
MYFLAGS=-Wall -Wextra -Wno-unused-parameter -Wno-sign-compare -Ilibavutil -I. -DCONFIG_MEMORY_POISONING=0 -DHAVE_FAST_UNALIGNED=0
MYCPPFLAGS=$(MYFLAGS)
MYCFLAGS=$(MYFLAGS) -Wno-pointer-sign
.PHONY: all clean
%.o: %.c
gcc $(CFLAGS) $(MYCFLAGS) $(OPTMODE) -c $< -o $@
%.o: %.cpp
gcc $(CFLAGS) $(MYCPPFLAGS) $(CXXFLAGS) $(OPTMODE) -c $< -o $@
$(TARGET): $(OBJS)
gcc $(OPTMODE) -o $(TARGET) \
$(OBJS) $(PROGMAIN) -lm -lstdc++
clean:
rm -f $(TARGET) $(OBJS) README
README: readme_unformatted.txt
fmt -s readme_unformatted.txt > README
all: $(TARGET) README

495
dreamcast/pvrtex/README Normal file
View File

@ -0,0 +1,495 @@
pvrtex converts images to Dreamcast PowerVR textures.
It is designed to work similarly to tvspelsfreak's texconv, so it can
be used in place of texconv will minimal changes. It might be helpful
to read the readme for texconv for additional information not covered
here. In particular, there are explainations of the types of textures
supported by the Dreamcast.
Compared to texconv, pvrtex has the following enhancements:
* Faster texture compression and palette generation
* Can generate small codebook VQ textures
* No Qt dependency
* Support for compressed stride textures
* Better mipmap generation
* Dithering
* Support for additional output file types (adds .PVR and .DT)
--------------------------------------------------------------------------
Usage Examples:
pvrtex -i source.png -o texture.dt
Converts a PNG file to a DT file that is twiddled, uncompressed
texture, without mipmaps. The format is automatically chosen
depending on alpha content of source.png (See description for
AUTO texture format in command option listing).
pvrtex -i source.png -o texture.dt -f argb4444 -d -c 64 -m quality -r -R
Converts a PNG file to a DT file that is twiddled, compressed
texture, with mipmaps. The texture will use the ARGB4444 color
format, and dithered. If the source image is not already a
square power-of-two, it will be resized to be the nearest square
power-of-two. The codebook used by the compression will be limited
to 64 entries out of the potential 256; this reduces quality, but
reduces the size of the texture by 1.5 KB, and improves fillrate.
pvrtex -i source.png -o texture.dt -f normal -m
Converts a PNG file containing a normal map to a DT file,
with mipmaps.
pvrtex -i source.png -o texture.dt -s
Converts a PNG file to a nontwiddled texture. source.png is not
required to be a power-of-two width, and can also be any multiple
of 32 that is <= 1024.
pvrtex -i source.png -o texture.dt -f pal8bpp -C 64 -d -p preview.png
Converts a PNG file to a DT file with 8-bit color. The resulting
image will not use more than 64 colors out of the potential 256,
and will be dithered. The pallete for the texture will be written
to texture.dt.pal. A preview of the resulting texture will be
written to preview.png.
pvrtex -i mip256.png -i mip128.png -i mip64.png -i mip32.png -i mip16.png
-o texture.dt -m
Generates a mipmapped texture, using the different input images
as user defined mipmap levels instead of automatically generating
all of them. If a mipmap level is not defined by the user, it
will be generated from a higher level. By default, the higher
level will not be the level above, but three levels above; if you
want to use the level above, use fast mipmaps (-m fast) instead.
--------------------------------------------------------------------------
Building:
Run "make".
:-|
To generate the proper README with linebreaks, from
readme_unformatted.txt, run "make README" or "make all". Requires
"fmt".
--------------------------------------------------------------------------
Command Line Options:
--help, -h
Displays help
--version, -V
Displays version
--in [filename], -i [filename]
Input image file. This option is required.
If multiple input images are specified, they are currently
assumed to be different mipmap levels for a single texture. Resize
options can not be used for custom mipmaps, so all images must
be a square power-of-two.
Uses stb_image library for reading the image. The supported
formats are:
JPEG, PNG, TGA, BMP, PSD, GIF, HDR, PIC, PMN
--out [filename], -o [filename]
Sets the file name of the converted texture. The extension of
this filename controls the file format.
The supported formats are:
.PVR
Official PowerVR texture. This is incomplete as was
created to help test the output of the converter, by
using PC PVR viewing programs to check the resulting
texture. There are likely incompatiblities with .PVR
handling of official games (for example, pvrtex does
not add a GBIX chunk).
.TEX
Format used by tvspelsfreak's texconv. pvrtex will
generate certain formats not supported by texconv but
representable in the file format (like compressed stride
textures).
.DT
New file format used by this program. Supports small
codebook VQ, and texture data is aligned to a 32-byte
boundry to make DMA easier.
It's possible to specify no output file if only a preview image
is desired.
--format [type], -f [type]
Sets the pixel format of the resulting texture.
[type] can be one of the following:
RGB565
Best color out of standard formats without sacrificing
speed, but can have rainbowing on grayscale images.
ARGB1555
Allows for fully transparent texels. Better choice for
grayscale than RGB565, which can have rainbowing.
ARGB4444
Allows for alpha gradients, but poorest color depth with
noticable banding.
YUV422 / YUV
Better than RGB565 or ARGB1555 for gradients, but
bi/trilinear filtering has worse performance.
PAL8BPP
Maximum of 256 colors. Palette is generated as a seperate
file. Must be twiddled. If compression, mipmapping,
and bi/trilinear are used, a hardware bug causes some
texels to be filtered incorrectly on the top left/bottom
right corners of a 4x2 block.
PAL4BPP
Maximum of 16 colors. Palette is generated as a seperate
file. Must be twiddled. If compression, mipmapping,
and bi/trilinear are used, a hardware bug causes some
texels to be filtered incorrectly on the top left/bottom
right corners of a 4x4 block
BUMPMAP
Generates PVR normal map. Source image is treated as a
height map.
NORMAL
Generates PVR normal map. Source image is treated as a
DOT3 normal map, with RGB channels corresponding to the
normal's XYZ.
AUTO
Selects RGB565, ARGB1555, or ARGB4444 depending on alpha
content of input image. If fully opaque, RGB565 is used,
all pixels have either fully opaque or fully transparent
alpha, ARGB1555 is used, and if some pixels have non-fully
opaque or transparent pixels, ARGB4444 is used.
AUTOYUV
Same as AUTO, but usage of RGB565 is replaced with YUV422.
--preview [filename], -p [filename]
Generates a preview of the resulting texture file. You can
see the results of bit depth reduction, dithering, mipmaps,
and compression.
The preview is can be a PNG, JPG, BMP, or TGA file. Preview
JPGs are generally not a good choice do to the lack of alpha,
and possiblility of compression artifacts.
--compress [codebook_size / "small"], -c [codebook_size / "small"]
Generates a VQ compressed texture.
codebook_size is an optional parameter adjusts the size of the
codebook generated for the texture. Reducing the codebook_size
can improve fillrate, and, with .PVR and .DT files, improve the
compression ratio of small textures. By default, a full codebook
is used to generate the best quality texture. codebook_size can
be a number from 1 to 256, or the string "small".
For .PVR files, using a number will never reduce the size of
the texture, but can improve fillrate. Specifying "small" as the
codebook size will reduce the texture size for certain textures
smaller than 64x64 without mipmaps, or 32x32 with mipmaps.
For .TEX files, codebook_size will never reduce the size of the
texture, but can still improve performance.
For .DT files, reducing the codebook_size will reduce the size
of the texture. Specifying "small" as the codebook size will
select a smaller codebook automatically for textures that are
128x128 or smaller, with a size of 192 for a 128x128 mipmapped
texture, down to 10 for an 8x8 non-mipped texture.
--max-color [colors], -C [colors]
This limits the number of colors used for a PAL8BPP or PAL4BPP
format texture. This option could be used to generate an 8BPP
texture that only uses 64 colors, and the unused colors could
be used for other textures.
--mipmap ["fast"], -m ["fast"]
With this option, the resulting file will have mipmaps.
By default, a Mitchell-Netravalli filter will be used on a level
3 steps above. (e.g. 64x64 will be generated from 512x512)
Adding the parameter "fast" to this option, each mipmap level
is generated by downsampling the level above. (e.g. 64x64 level
will be generated from 128x128) This speeds up mipmap generation
for large textures.
When generating high quality mipmaps for a resized image,
the largest mips will be generated directly from the source
image. (i.e. with a 1600x500 source image, converted with "-m
-r near -R x2", pvrtex will create a 1024x1024 texture, with
mipmap levels 512x512 and 256x256 generated directly from the
1600x500 source, and not the 1024x1024 top most level).
--no-mip-shift, -S
When generating mipmaps, by default, pvrtex will preform a
subpixel adjustment during downsampling to ensure the mipmaps
line up correctly. This option will disable this.
--perfect-mip [levels], -M [levels]
When using mipmaps and compression, small mipmap levels will be
loselessly compressed.
The levels parameter controls how many levels are lossless. 1
means only the 1x1 level will be losslessly compressed, 2 means
1x1 and 2x2 will be loselessly compressed, and so on.
Generating lossless mipmaps use up VQ codebook slots. These are
the total number of codebook entries used for a given number of
lossless mipmaps:
16-bit 8-bit 4-bit
1 level (1x1) 1 1 1
2 levels (2x2) 2 1 1
3 levels (4x4) 6 3 2
4 levels (8x8) 22 11 6
5 levels (16x16) 86 43 22
6 levels (32x32) -- 171 86
--high-weight [levels], -H [levels]
When using mipmaps and compression, this increases the weight
the compressor gives smaller mipmap levels, to encourage the
compressor to generate them at higher quality, at the cost of
lower quality higher levels. Not currently supported for 4BPP
textures.
The levels parameter controls how many levels below the largest
have extra weight. A value of 1 means every level besides the
top has boosted weight, a value of 2 means the two largest levels
have normal weight, while every smaller level is boosted.
--dither [amount], -d [amount]
Enables dithering. Currently, Floyd-Steinberg is used.
Amount is an optional parameter that adjusts the amount of
dithering, and is a decimal value from 0 to 1. 0 will result in
no dithering, while 1 results in full dithering. If dithering
is enabled but an amount is not specified, full dithering is used.
This option has no effect on YUV textures, but is valid on
all others.
--stride, -s
Output a non-twiddled texture. This also allows for
non-power-of-two sized textures. Width must still 8, 16, or
a multiple of 32 less than or equal to 1024. Any height can be
used, from 1 to 1024.
If a texture has a power-of-two dimensions and --stride is
used, the resulting texture will be a nontwiddled texture that
can be rendered without stride, for formats that support such
textures. (.DT and .TEX support this, .PVR does not.)
Stride textures do not wrap as normal if the width is not
a power-of-two, and have worse rendering performance than
twiddled textures (especially when filtered or rotated). It
is not possible to generate palettized or normal textures with
stride. .PVR files cannnot use stride.
Valid widths for stride texture:
8, 16, 32, 64, 96, 128, 160, 192, 224, 256, 320, 352,
384, 416, 480, 512, 544, 576, 608, 640, 672, 704, 736,
768, 800, 832, 864, 896, 928, 960, 992, 1024
--resize [method], -r [method]
Resize a input image that is not a supported PVR texture size
to a valid size.
If the texture is not strided, the texture will be resized to
a power-of-two on both dimensions. For stride textures, width
will be adjusted to an appropriate stride size, and the height
will always be resized to a power-of-two.
Method controls how the image will be resized.
NONE
Generates an error if input image is not a valid
size. This is the default.
NEAR
Round size up or down to nearest valid size. If resize is
enabled, but no method is specified, this is the default.
UP
Round size up to next valid size
DOWN
Round size down to next valid size
Examples for non-stride textures:
Source size NONE NEAR UP DOWN
256x256 256x256 256x256 256x256 256x256
260x260 Error 256x256 512x512 256x256
200x200 Error 256x256 256x256 128x128
200x260 Error 256x256 256x512 128x256
2000x2000 Error 1024x1024 1024x1024 1024x1024
1x1 Error 8x8 8x8 8x8
--mip-resize [method], -R [method]
When using mipmaps, resizes nonsquare images to be square. This
option does nothing if not using mipmaps or the image is already
square (after --resize). This new size calculation occurs after
the standard --resize. Source images are only resized once. This
option will not resize the image to a power-of-two size if it's
not already (use --resize for that).
Method controls how the image will be resized.
NONE
Generates an error if input image is not a valid mipmap
size. This is the default.
X2
Doubles the narrower dimension. A 256x32 image will
be resized to 64x64. If mip-resize is enabled, but no
method is specified, this is the default.
X4
Quaduples or doubles the narrower dimension. A 256x32
image will be resized to 128x128.
UP
Resizes the narrower dimension to be the same size as
the wider. A 256x32 image will be resized to 256x256.
DOWN
Resizes the wider dimension to be the same size as the
narrower. A 256x32 image will be resized to 32x32.
Examples:
Source size X2 X4 UP DOWN
256x256 256x256 256x256 256x256 256x256
256x128 256x256 256x256 256x256 128x128
256x64 128x128 256x256 256x256 64x64
256x32 64x64 128x128 256x256 32x32
1024x8 16x16 32x32 1024x1024 8x8
--edge [type], -e [type]
Controls how the edges of the image are handled when
resizing. This also affects height map to normal map conversion.
Valid options:
CLAMP
Samples are clamped to edge of image, default if not
mipmapped.
WRAP
Samples wrap around to other side of image, default if
mipmaps are used. Is works well when the texture is used
to that it repeats, but might cause noticble bleeding
around the edge of the texture in certain situations. For
example, a poster or sign that doesn't repeat across
the polygon. In that cause, CLAMP should be used.
REFLECT
Samples reflect off edge of image back into valid area. If
you use are planning on using UV mirroring instead of
wrapping, use this instead of wrap.
ZERO
Outside of image is treated as transparent blackness. This
is not currently supported for images with a --type
of BUMPMAP.
The default is CLAMP if not using mipmaps, or WRAP if mipmaps
are used.
--bilinear, -b
In texconv, this was used to generate mipmaps with a box
filter. This option is ignored in pvrtex, which currently always
uses a Mitchell-Netravalli filter.
--nearest, -n
In texconv, this was used to generate mipmaps by point
sampling. This option is not supported in pvrtex, and will cause
pvrtex to abort.
--vqcodeusage (Not supported)
This option from texconv is not recognized at all by pvrtex.
--------------------------------------------------------------------------
.DT File Format
See file_dctex.h for documentation. file_dctex.h can also be used
as a library to help access information from the file's header.
--------------------------------------------------------------------------
Known bugs
High weight compressed mips (--high-weight) does not currently
work with 4BPP textures; the parameter will be ignored if
specified.
There is a weird pathological performance issue with compression
on certain textures. With a 1024x1024 texture with mips that
is ff000000 on the left side, and ffffffff on the right side,
performance drops by around 15x. It is apparently cache missing
constantly, according to cachegrind, in a function that reads
memory linearly (distance_limited in elbg.c). Doesn't make sense.
--------------------------------------------------------------------------
Future Ideas
* Code clean up
* Add Yliluoma dithering
* Improve error checking
* It might be possible to improve VQ quality by compressing
4/5/6 bit color instead of 8 bit (so the compressor won't waste
codebook space on colors that are too similar to distinguish at
given bit depth)
* Speed up compression by not processing alpha for opaque textures
* Add ability to generate a single palette to be shared for
multiple textures
* Allow specifying palette format
* Allow specifiying custom external palette for texture
* Add ability to generate animated VQ textures with shared
codebook for all frames
* Auto generate output name (e.g. -i image.png -o $.dt will
output image.dt)
* Add wildcard input files (e.g. -i *.png)
* Add VQ index dithering
* Add KIMG output support
* Allow texture formats (PVR/TEX/DT/KMG) to be used as input
formats, to transcode or recompress textures
* Add per-axis edge sampling control
* Rework code so that this can be used as a library
* Allow sizing to arbitary size (i.e. --resize 256x256)
* Add a filter to try to hide the palettized compressed mipmap bug
--------------------------------------------------------------------------
History:
Version 1.01
Program now displays error message when an error occurs
loading a source image. Previously, the program would
hit an assertion.
Fixed "--resize down" option. Previously, the program
would round down sizes that were already a power-of-two,
now sizes that are already POT are left unchanged.
Included a missing FFmpeg header file.
Version 1.0
Initial release
--------------------------------------------------------------------------
License:
This uses code from FFmpeg, which is LGPL, so this project is
also LGPL. Files not originating from FFmpeg can also be used
as public domain code/BSD/MIT/whatever.

View File

@ -0,0 +1 @@
# pvrtex

463
dreamcast/pvrtex/avstring.c Normal file
View File

@ -0,0 +1,463 @@
/*
* Copyright (c) 2000, 2001, 2002 Fabrice Bellard
* Copyright (c) 2007 Mans Rullgard
*
* 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
*/
#include <limits.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "config.h"
#include "mem.h"
#include "avassert.h"
#include "avstring.h"
#include "bprint.h"
#include "error.h"
#include "macros.h"
#include "version.h"
int av_strstart(const char *str, const char *pfx, const char **ptr)
{
while (*pfx && *pfx == *str) {
pfx++;
str++;
}
if (!*pfx && ptr)
*ptr = str;
return !*pfx;
}
int av_stristart(const char *str, const char *pfx, const char **ptr)
{
while (*pfx && av_toupper((unsigned)*pfx) == av_toupper((unsigned)*str)) {
pfx++;
str++;
}
if (!*pfx && ptr)
*ptr = str;
return !*pfx;
}
char *av_stristr(const char *s1, const char *s2)
{
if (!*s2)
return (char*)(intptr_t)s1;
do
if (av_stristart(s1, s2, NULL))
return (char*)(intptr_t)s1;
while (*s1++);
return NULL;
}
char *av_strnstr(const char *haystack, const char *needle, size_t hay_length)
{
size_t needle_len = strlen(needle);
if (!needle_len)
return (char*)haystack;
while (hay_length >= needle_len) {
hay_length--;
if (!memcmp(haystack, needle, needle_len))
return (char*)haystack;
haystack++;
}
return NULL;
}
size_t av_strlcpy(char *dst, const char *src, size_t size)
{
size_t len = 0;
while (++len < size && *src)
*dst++ = *src++;
if (len <= size)
*dst = 0;
return len + strlen(src) - 1;
}
size_t av_strlcat(char *dst, const char *src, size_t size)
{
size_t len = strlen(dst);
if (size <= len + 1)
return len + strlen(src);
return len + av_strlcpy(dst + len, src, size - len);
}
size_t av_strlcatf(char *dst, size_t size, const char *fmt, ...)
{
size_t len = strlen(dst);
va_list vl;
va_start(vl, fmt);
len += vsnprintf(dst + len, size > len ? size - len : 0, fmt, vl);
va_end(vl);
return len;
}
char *av_asprintf(const char *fmt, ...)
{
char *p = NULL;
va_list va;
int len;
va_start(va, fmt);
len = vsnprintf(NULL, 0, fmt, va);
va_end(va);
if (len < 0)
goto end;
p = av_malloc(len + 1);
if (!p)
goto end;
va_start(va, fmt);
len = vsnprintf(p, len + 1, fmt, va);
va_end(va);
if (len < 0)
av_freep(&p);
end:
return p;
}
#define WHITESPACES " \n\t\r"
char *av_get_token(const char **buf, const char *term)
{
char *out = av_malloc(strlen(*buf) + 1);
char *ret = out, *end = out;
const char *p = *buf;
if (!out)
return NULL;
p += strspn(p, WHITESPACES);
while (*p && !strspn(p, term)) {
char c = *p++;
if (c == '\\' && *p) {
*out++ = *p++;
end = out;
} else if (c == '\'') {
while (*p && *p != '\'')
*out++ = *p++;
if (*p) {
p++;
end = out;
}
} else {
*out++ = c;
}
}
do
*out-- = 0;
while (out >= end && strspn(out, WHITESPACES));
*buf = p;
return ret;
}
char *av_strtok(char *s, const char *delim, char **saveptr)
{
char *tok;
if (!s && !(s = *saveptr))
return NULL;
/* skip leading delimiters */
s += strspn(s, delim);
/* s now points to the first non delimiter char, or to the end of the string */
if (!*s) {
*saveptr = NULL;
return NULL;
}
tok = s++;
/* skip non delimiters */
s += strcspn(s, delim);
if (*s) {
*s = 0;
*saveptr = s+1;
} else {
*saveptr = NULL;
}
return tok;
}
int av_strcasecmp(const char *a, const char *b)
{
uint8_t c1, c2;
do {
c1 = av_tolower(*a++);
c2 = av_tolower(*b++);
} while (c1 && c1 == c2);
return c1 - c2;
}
int av_strncasecmp(const char *a, const char *b, size_t n)
{
uint8_t c1, c2;
if (n <= 0)
return 0;
do {
c1 = av_tolower(*a++);
c2 = av_tolower(*b++);
} while (--n && c1 && c1 == c2);
return c1 - c2;
}
char *av_strireplace(const char *str, const char *from, const char *to)
{
char *ret = NULL;
const char *pstr2, *pstr = str;
size_t tolen = strlen(to), fromlen = strlen(from);
AVBPrint pbuf;
av_bprint_init(&pbuf, 1, AV_BPRINT_SIZE_UNLIMITED);
while ((pstr2 = av_stristr(pstr, from))) {
av_bprint_append_data(&pbuf, pstr, pstr2 - pstr);
pstr = pstr2 + fromlen;
av_bprint_append_data(&pbuf, to, tolen);
}
av_bprint_append_data(&pbuf, pstr, strlen(pstr));
if (!av_bprint_is_complete(&pbuf)) {
av_bprint_finalize(&pbuf, NULL);
} else {
av_bprint_finalize(&pbuf, &ret);
}
return ret;
}
const char *av_basename(const char *path)
{
char *p;
#if HAVE_DOS_PATHS
char *q, *d;
#endif
if (!path || *path == '\0')
return ".";
p = strrchr(path, '/');
#if HAVE_DOS_PATHS
q = strrchr(path, '\\');
d = strchr(path, ':');
p = FFMAX3(p, q, d);
#endif
if (!p)
return path;
return p + 1;
}
const char *av_dirname(char *path)
{
char *p = path ? strrchr(path, '/') : NULL;
#if HAVE_DOS_PATHS
char *q = path ? strrchr(path, '\\') : NULL;
char *d = path ? strchr(path, ':') : NULL;
d = d ? d + 1 : d;
p = FFMAX3(p, q, d);
#endif
if (!p)
return ".";
*p = '\0';
return path;
}
char *av_append_path_component(const char *path, const char *component)
{
size_t p_len, c_len;
char *fullpath;
if (!path)
return av_strdup(component);
if (!component)
return av_strdup(path);
p_len = strlen(path);
c_len = strlen(component);
if (p_len > SIZE_MAX - c_len || p_len + c_len > SIZE_MAX - 2)
return NULL;
fullpath = av_malloc(p_len + c_len + 2);
if (fullpath) {
if (p_len) {
av_strlcpy(fullpath, path, p_len + 1);
if (c_len) {
if (fullpath[p_len - 1] != '/' && component[0] != '/')
fullpath[p_len++] = '/';
else if (fullpath[p_len - 1] == '/' && component[0] == '/')
p_len--;
}
}
av_strlcpy(&fullpath[p_len], component, c_len + 1);
fullpath[p_len + c_len] = 0;
}
return fullpath;
}
int av_escape(char **dst, const char *src, const char *special_chars,
enum AVEscapeMode mode, int flags)
{
AVBPrint dstbuf;
int ret;
av_bprint_init(&dstbuf, 1, INT_MAX); /* (int)dstbuf.len must be >= 0 */
av_bprint_escape(&dstbuf, src, special_chars, mode, flags);
if (!av_bprint_is_complete(&dstbuf)) {
av_bprint_finalize(&dstbuf, NULL);
return AVERROR(ENOMEM);
}
if ((ret = av_bprint_finalize(&dstbuf, dst)) < 0)
return ret;
return dstbuf.len;
}
int av_match_name(const char *name, const char *names)
{
const char *p;
int len, namelen;
if (!name || !names)
return 0;
namelen = strlen(name);
while (*names) {
int negate = '-' == *names;
p = strchr(names, ',');
if (!p)
p = names + strlen(names);
names += negate;
len = FFMAX(p - names, namelen);
if (!av_strncasecmp(name, names, len) || !strncmp("ALL", names, FFMAX(3, p - names)))
return !negate;
names = p + (*p == ',');
}
return 0;
}
int av_utf8_decode(int32_t *codep, const uint8_t **bufp, const uint8_t *buf_end,
unsigned int flags)
{
const uint8_t *p = *bufp;
uint32_t top;
uint64_t code;
int ret = 0, tail_len;
uint32_t overlong_encoding_mins[6] = {
0x00000000, 0x00000080, 0x00000800, 0x00010000, 0x00200000, 0x04000000,
};
if (p >= buf_end)
return 0;
code = *p++;
/* first sequence byte starts with 10, or is 1111-1110 or 1111-1111,
which is not admitted */
if ((code & 0xc0) == 0x80 || code >= 0xFE) {
ret = AVERROR(EILSEQ);
goto end;
}
top = (code & 128) >> 1;
tail_len = 0;
while (code & top) {
int tmp;
tail_len++;
if (p >= buf_end) {
(*bufp) ++;
return AVERROR(EILSEQ); /* incomplete sequence */
}
/* we assume the byte to be in the form 10xx-xxxx */
tmp = *p++ - 128; /* strip leading 1 */
if (tmp>>6) {
(*bufp) ++;
return AVERROR(EILSEQ);
}
code = (code<<6) + tmp;
top <<= 5;
}
code &= (top << 1) - 1;
/* check for overlong encodings */
av_assert0(tail_len <= 5);
if (code < overlong_encoding_mins[tail_len]) {
ret = AVERROR(EILSEQ);
goto end;
}
if (code >= 1U<<31) {
ret = AVERROR(EILSEQ); /* out-of-range value */
goto end;
}
*codep = code;
if (code > 0x10FFFF &&
!(flags & AV_UTF8_FLAG_ACCEPT_INVALID_BIG_CODES))
ret = AVERROR(EILSEQ);
if (code < 0x20 && code != 0x9 && code != 0xA && code != 0xD &&
flags & AV_UTF8_FLAG_EXCLUDE_XML_INVALID_CONTROL_CODES)
ret = AVERROR(EILSEQ);
if (code >= 0xD800 && code <= 0xDFFF &&
!(flags & AV_UTF8_FLAG_ACCEPT_SURROGATES))
ret = AVERROR(EILSEQ);
if ((code == 0xFFFE || code == 0xFFFF) &&
!(flags & AV_UTF8_FLAG_ACCEPT_NON_CHARACTERS))
ret = AVERROR(EILSEQ);
end:
*bufp = p;
return ret;
}
int av_match_list(const char *name, const char *list, char separator)
{
const char *p, *q;
for (p = name; p && *p; ) {
for (q = list; q && *q; ) {
int k;
for (k = 0; p[k] == q[k] || (p[k]*q[k] == 0 && p[k]+q[k] == separator); k++)
if (k && (!p[k] || p[k] == separator))
return 1;
q = strchr(q, separator);
q += !!q;
}
p = strchr(p, separator);
p += !!p;
}
return 0;
}

332
dreamcast/pvrtex/bprint.c Normal file
View File

@ -0,0 +1,332 @@
/*
* Copyright (c) 2012 Nicolas George
*
* 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
*/
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "avstring.h"
#include "bprint.h"
#include "compat/va_copy.h"
#include "error.h"
#include "macros.h"
#include "mem.h"
#define av_bprint_room(buf) ((buf)->size - FFMIN((buf)->len, (buf)->size))
#define av_bprint_is_allocated(buf) ((buf)->str != (buf)->reserved_internal_buffer)
static int av_bprint_alloc(AVBPrint *buf, unsigned room)
{
char *old_str, *new_str;
unsigned min_size, new_size;
if (buf->size == buf->size_max)
return AVERROR(EIO);
if (!av_bprint_is_complete(buf))
return AVERROR_INVALIDDATA; /* it is already truncated anyway */
min_size = buf->len + 1 + FFMIN(UINT_MAX - buf->len - 1, room);
new_size = buf->size > buf->size_max / 2 ? buf->size_max : buf->size * 2;
if (new_size < min_size)
new_size = FFMIN(buf->size_max, min_size);
old_str = av_bprint_is_allocated(buf) ? buf->str : NULL;
new_str = av_realloc(old_str, new_size);
if (!new_str)
return AVERROR(ENOMEM);
if (!old_str)
memcpy(new_str, buf->str, buf->len + 1);
buf->str = new_str;
buf->size = new_size;
return 0;
}
static void av_bprint_grow(AVBPrint *buf, unsigned extra_len)
{
/* arbitrary margin to avoid small overflows */
extra_len = FFMIN(extra_len, UINT_MAX - 5 - buf->len);
buf->len += extra_len;
if (buf->size)
buf->str[FFMIN(buf->len, buf->size - 1)] = 0;
}
void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)
{
unsigned size_auto = (char *)buf + sizeof(*buf) -
buf->reserved_internal_buffer;
if (size_max == 1)
size_max = size_auto;
buf->str = buf->reserved_internal_buffer;
buf->len = 0;
buf->size = FFMIN(size_auto, size_max);
buf->size_max = size_max;
*buf->str = 0;
if (size_init > buf->size)
av_bprint_alloc(buf, size_init - 1);
}
void av_bprint_init_for_buffer(AVBPrint *buf, char *buffer, unsigned size)
{
buf->str = buffer;
buf->len = 0;
buf->size = size;
buf->size_max = size;
*buf->str = 0;
}
void av_bprintf(AVBPrint *buf, const char *fmt, ...)
{
unsigned room;
char *dst;
va_list vl;
int extra_len;
while (1) {
room = av_bprint_room(buf);
dst = room ? buf->str + buf->len : NULL;
va_start(vl, fmt);
extra_len = vsnprintf(dst, room, fmt, vl);
va_end(vl);
if (extra_len <= 0)
return;
if (extra_len < room)
break;
if (av_bprint_alloc(buf, extra_len))
break;
}
av_bprint_grow(buf, extra_len);
}
void av_vbprintf(AVBPrint *buf, const char *fmt, va_list vl_arg)
{
unsigned room;
char *dst;
int extra_len;
va_list vl;
while (1) {
room = av_bprint_room(buf);
dst = room ? buf->str + buf->len : NULL;
va_copy(vl, vl_arg);
extra_len = vsnprintf(dst, room, fmt, vl);
va_end(vl);
if (extra_len <= 0)
return;
if (extra_len < room)
break;
if (av_bprint_alloc(buf, extra_len))
break;
}
av_bprint_grow(buf, extra_len);
}
void av_bprint_chars(AVBPrint *buf, char c, unsigned n)
{
unsigned room, real_n;
while (1) {
room = av_bprint_room(buf);
if (n < room)
break;
if (av_bprint_alloc(buf, n))
break;
}
if (room) {
real_n = FFMIN(n, room - 1);
memset(buf->str + buf->len, c, real_n);
}
av_bprint_grow(buf, n);
}
void av_bprint_append_data(AVBPrint *buf, const char *data, unsigned size)
{
unsigned room, real_n;
while (1) {
room = av_bprint_room(buf);
if (size < room)
break;
if (av_bprint_alloc(buf, size))
break;
}
if (room) {
real_n = FFMIN(size, room - 1);
memcpy(buf->str + buf->len, data, real_n);
}
av_bprint_grow(buf, size);
}
void av_bprint_strftime(AVBPrint *buf, const char *fmt, const struct tm *tm)
{
unsigned room;
size_t l;
if (!*fmt)
return;
while (1) {
room = av_bprint_room(buf);
if (room && (l = strftime(buf->str + buf->len, room, fmt, tm)))
break;
/* strftime does not tell us how much room it would need: let us
retry with twice as much until the buffer is large enough */
room = !room ? strlen(fmt) + 1 :
room <= INT_MAX / 2 ? room * 2 : INT_MAX;
if (av_bprint_alloc(buf, room)) {
/* impossible to grow, try to manage something useful anyway */
room = av_bprint_room(buf);
if (room < 1024) {
/* if strftime fails because the buffer has (almost) reached
its maximum size, let us try in a local buffer; 1k should
be enough to format any real date+time string */
char buf2[1024];
if ((l = strftime(buf2, sizeof(buf2), fmt, tm))) {
av_bprintf(buf, "%s", buf2);
return;
}
}
if (room) {
/* if anything else failed and the buffer is not already
truncated, let us add a stock string and force truncation */
static const char txt[] = "[truncated strftime output]";
memset(buf->str + buf->len, '!', room);
memcpy(buf->str + buf->len, txt, FFMIN(sizeof(txt) - 1, room));
av_bprint_grow(buf, room); /* force truncation */
}
return;
}
}
av_bprint_grow(buf, l);
}
void av_bprint_get_buffer(AVBPrint *buf, unsigned size,
unsigned char **mem, unsigned *actual_size)
{
if (size > av_bprint_room(buf))
av_bprint_alloc(buf, size);
*actual_size = av_bprint_room(buf);
*mem = *actual_size ? buf->str + buf->len : NULL;
}
void av_bprint_clear(AVBPrint *buf)
{
if (buf->len) {
*buf->str = 0;
buf->len = 0;
}
}
int av_bprint_finalize(AVBPrint *buf, char **ret_str)
{
unsigned real_size = FFMIN(buf->len + 1, buf->size);
char *str;
int ret = 0;
if (ret_str) {
if (av_bprint_is_allocated(buf)) {
str = av_realloc(buf->str, real_size);
if (!str)
str = buf->str;
buf->str = NULL;
} else {
str = av_memdup(buf->str, real_size);
if (!str)
ret = AVERROR(ENOMEM);
}
*ret_str = str;
} else {
if (av_bprint_is_allocated(buf))
av_freep(&buf->str);
}
buf->size = real_size;
return ret;
}
#define WHITESPACES " \n\t\r"
void av_bprint_escape(AVBPrint *dstbuf, const char *src, const char *special_chars,
enum AVEscapeMode mode, int flags)
{
const char *src0 = src;
if (mode == AV_ESCAPE_MODE_AUTO)
mode = AV_ESCAPE_MODE_BACKSLASH; /* TODO: implement a heuristic */
switch (mode) {
case AV_ESCAPE_MODE_QUOTE:
/* enclose the string between '' */
av_bprint_chars(dstbuf, '\'', 1);
for (; *src; src++) {
if (*src == '\'')
av_bprintf(dstbuf, "'\\''");
else
av_bprint_chars(dstbuf, *src, 1);
}
av_bprint_chars(dstbuf, '\'', 1);
break;
case AV_ESCAPE_MODE_XML:
/* escape XML non-markup character data as per 2.4 by default: */
/* [^<&]* - ([^<&]* ']]>' [^<&]*) */
/* additionally, given one of the AV_ESCAPE_FLAG_XML_* flags, */
/* escape those specific characters as required. */
for (; *src; src++) {
switch (*src) {
case '&' : av_bprintf(dstbuf, "%s", "&amp;"); break;
case '<' : av_bprintf(dstbuf, "%s", "&lt;"); break;
case '>' : av_bprintf(dstbuf, "%s", "&gt;"); break;
case '\'':
if (!(flags & AV_ESCAPE_FLAG_XML_SINGLE_QUOTES))
goto XML_DEFAULT_HANDLING;
av_bprintf(dstbuf, "%s", "&apos;");
break;
case '"' :
if (!(flags & AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES))
goto XML_DEFAULT_HANDLING;
av_bprintf(dstbuf, "%s", "&quot;");
break;
XML_DEFAULT_HANDLING:
default: av_bprint_chars(dstbuf, *src, 1);
}
}
break;
/* case AV_ESCAPE_MODE_BACKSLASH or unknown mode */
default:
/* \-escape characters */
for (; *src; src++) {
int is_first_last = src == src0 || !*(src+1);
int is_ws = !!strchr(WHITESPACES, *src);
int is_strictly_special = special_chars && strchr(special_chars, *src);
int is_special =
is_strictly_special || strchr("'\\", *src) ||
(is_ws && (flags & AV_ESCAPE_FLAG_WHITESPACE));
if (is_strictly_special ||
(!(flags & AV_ESCAPE_FLAG_STRICT) &&
(is_special || (is_ws && is_first_last))))
av_bprint_chars(dstbuf, '\\', 1);
av_bprint_chars(dstbuf, *src, 1);
}
break;
}
}

View File

@ -0,0 +1,34 @@
/*
* MSVC Compatible va_copy macro
* Copyright (c) 2012 Derek Buitenhuis
*
* 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
*/
#ifndef COMPAT_VA_COPY_H
#define COMPAT_VA_COPY_H
#include <stdarg.h>
#if !defined(va_copy) && defined(_MSC_VER)
#define va_copy(dst, src) ((dst) = (src))
#endif
#if !defined(va_copy) && defined(__GNUC__) && __GNUC__ < 3
#define va_copy(dst, src) __va_copy(dst, src)
#endif
#endif /* COMPAT_VA_COPY_H */

View File

415
dreamcast/pvrtex/crc.c Normal file
View File

@ -0,0 +1,415 @@
/*
* copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
*
* 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
*/
#include "config.h"
#include "thread.h"
#include "avassert.h"
#include "bswap.h"
#include "crc.h"
#include "error.h"
#if CONFIG_HARDCODED_TABLES
static const AVCRC av_crc_table[AV_CRC_MAX][257] = {
[AV_CRC_8_ATM] = {
0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31,
0x24, 0x23, 0x2A, 0x2D, 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65,
0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, 0xE0, 0xE7, 0xEE, 0xE9,
0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1,
0xB4, 0xB3, 0xBA, 0xBD, 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2,
0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, 0xB7, 0xB0, 0xB9, 0xBE,
0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16,
0x03, 0x04, 0x0D, 0x0A, 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42,
0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, 0x89, 0x8E, 0x87, 0x80,
0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8,
0xDD, 0xDA, 0xD3, 0xD4, 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C,
0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, 0x19, 0x1E, 0x17, 0x10,
0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F,
0x6A, 0x6D, 0x64, 0x63, 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B,
0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, 0xAE, 0xA9, 0xA0, 0xA7,
0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF,
0xFA, 0xFD, 0xF4, 0xF3, 0x01
},
[AV_CRC_8_EBU] = {
0x00, 0x1D, 0x3A, 0x27, 0x74, 0x69, 0x4E, 0x53, 0xE8, 0xF5, 0xD2, 0xCF,
0x9C, 0x81, 0xA6, 0xBB, 0xCD, 0xD0, 0xF7, 0xEA, 0xB9, 0xA4, 0x83, 0x9E,
0x25, 0x38, 0x1F, 0x02, 0x51, 0x4C, 0x6B, 0x76, 0x87, 0x9A, 0xBD, 0xA0,
0xF3, 0xEE, 0xC9, 0xD4, 0x6F, 0x72, 0x55, 0x48, 0x1B, 0x06, 0x21, 0x3C,
0x4A, 0x57, 0x70, 0x6D, 0x3E, 0x23, 0x04, 0x19, 0xA2, 0xBF, 0x98, 0x85,
0xD6, 0xCB, 0xEC, 0xF1, 0x13, 0x0E, 0x29, 0x34, 0x67, 0x7A, 0x5D, 0x40,
0xFB, 0xE6, 0xC1, 0xDC, 0x8F, 0x92, 0xB5, 0xA8, 0xDE, 0xC3, 0xE4, 0xF9,
0xAA, 0xB7, 0x90, 0x8D, 0x36, 0x2B, 0x0C, 0x11, 0x42, 0x5F, 0x78, 0x65,
0x94, 0x89, 0xAE, 0xB3, 0xE0, 0xFD, 0xDA, 0xC7, 0x7C, 0x61, 0x46, 0x5B,
0x08, 0x15, 0x32, 0x2F, 0x59, 0x44, 0x63, 0x7E, 0x2D, 0x30, 0x17, 0x0A,
0xB1, 0xAC, 0x8B, 0x96, 0xC5, 0xD8, 0xFF, 0xE2, 0x26, 0x3B, 0x1C, 0x01,
0x52, 0x4F, 0x68, 0x75, 0xCE, 0xD3, 0xF4, 0xE9, 0xBA, 0xA7, 0x80, 0x9D,
0xEB, 0xF6, 0xD1, 0xCC, 0x9F, 0x82, 0xA5, 0xB8, 0x03, 0x1E, 0x39, 0x24,
0x77, 0x6A, 0x4D, 0x50, 0xA1, 0xBC, 0x9B, 0x86, 0xD5, 0xC8, 0xEF, 0xF2,
0x49, 0x54, 0x73, 0x6E, 0x3D, 0x20, 0x07, 0x1A, 0x6C, 0x71, 0x56, 0x4B,
0x18, 0x05, 0x22, 0x3F, 0x84, 0x99, 0xBE, 0xA3, 0xF0, 0xED, 0xCA, 0xD7,
0x35, 0x28, 0x0F, 0x12, 0x41, 0x5C, 0x7B, 0x66, 0xDD, 0xC0, 0xE7, 0xFA,
0xA9, 0xB4, 0x93, 0x8E, 0xF8, 0xE5, 0xC2, 0xDF, 0x8C, 0x91, 0xB6, 0xAB,
0x10, 0x0D, 0x2A, 0x37, 0x64, 0x79, 0x5E, 0x43, 0xB2, 0xAF, 0x88, 0x95,
0xC6, 0xDB, 0xFC, 0xE1, 0x5A, 0x47, 0x60, 0x7D, 0x2E, 0x33, 0x14, 0x09,
0x7F, 0x62, 0x45, 0x58, 0x0B, 0x16, 0x31, 0x2C, 0x97, 0x8A, 0xAD, 0xB0,
0xE3, 0xFE, 0xD9, 0xC4, 0x01
},
[AV_CRC_16_ANSI] = {
0x0000, 0x0580, 0x0F80, 0x0A00, 0x1B80, 0x1E00, 0x1400, 0x1180,
0x3380, 0x3600, 0x3C00, 0x3980, 0x2800, 0x2D80, 0x2780, 0x2200,
0x6380, 0x6600, 0x6C00, 0x6980, 0x7800, 0x7D80, 0x7780, 0x7200,
0x5000, 0x5580, 0x5F80, 0x5A00, 0x4B80, 0x4E00, 0x4400, 0x4180,
0xC380, 0xC600, 0xCC00, 0xC980, 0xD800, 0xDD80, 0xD780, 0xD200,
0xF000, 0xF580, 0xFF80, 0xFA00, 0xEB80, 0xEE00, 0xE400, 0xE180,
0xA000, 0xA580, 0xAF80, 0xAA00, 0xBB80, 0xBE00, 0xB400, 0xB180,
0x9380, 0x9600, 0x9C00, 0x9980, 0x8800, 0x8D80, 0x8780, 0x8200,
0x8381, 0x8601, 0x8C01, 0x8981, 0x9801, 0x9D81, 0x9781, 0x9201,
0xB001, 0xB581, 0xBF81, 0xBA01, 0xAB81, 0xAE01, 0xA401, 0xA181,
0xE001, 0xE581, 0xEF81, 0xEA01, 0xFB81, 0xFE01, 0xF401, 0xF181,
0xD381, 0xD601, 0xDC01, 0xD981, 0xC801, 0xCD81, 0xC781, 0xC201,
0x4001, 0x4581, 0x4F81, 0x4A01, 0x5B81, 0x5E01, 0x5401, 0x5181,
0x7381, 0x7601, 0x7C01, 0x7981, 0x6801, 0x6D81, 0x6781, 0x6201,
0x2381, 0x2601, 0x2C01, 0x2981, 0x3801, 0x3D81, 0x3781, 0x3201,
0x1001, 0x1581, 0x1F81, 0x1A01, 0x0B81, 0x0E01, 0x0401, 0x0181,
0x0383, 0x0603, 0x0C03, 0x0983, 0x1803, 0x1D83, 0x1783, 0x1203,
0x3003, 0x3583, 0x3F83, 0x3A03, 0x2B83, 0x2E03, 0x2403, 0x2183,
0x6003, 0x6583, 0x6F83, 0x6A03, 0x7B83, 0x7E03, 0x7403, 0x7183,
0x5383, 0x5603, 0x5C03, 0x5983, 0x4803, 0x4D83, 0x4783, 0x4203,
0xC003, 0xC583, 0xCF83, 0xCA03, 0xDB83, 0xDE03, 0xD403, 0xD183,
0xF383, 0xF603, 0xFC03, 0xF983, 0xE803, 0xED83, 0xE783, 0xE203,
0xA383, 0xA603, 0xAC03, 0xA983, 0xB803, 0xBD83, 0xB783, 0xB203,
0x9003, 0x9583, 0x9F83, 0x9A03, 0x8B83, 0x8E03, 0x8403, 0x8183,
0x8002, 0x8582, 0x8F82, 0x8A02, 0x9B82, 0x9E02, 0x9402, 0x9182,
0xB382, 0xB602, 0xBC02, 0xB982, 0xA802, 0xAD82, 0xA782, 0xA202,
0xE382, 0xE602, 0xEC02, 0xE982, 0xF802, 0xFD82, 0xF782, 0xF202,
0xD002, 0xD582, 0xDF82, 0xDA02, 0xCB82, 0xCE02, 0xC402, 0xC182,
0x4382, 0x4602, 0x4C02, 0x4982, 0x5802, 0x5D82, 0x5782, 0x5202,
0x7002, 0x7582, 0x7F82, 0x7A02, 0x6B82, 0x6E02, 0x6402, 0x6182,
0x2002, 0x2582, 0x2F82, 0x2A02, 0x3B82, 0x3E02, 0x3402, 0x3182,
0x1382, 0x1602, 0x1C02, 0x1982, 0x0802, 0x0D82, 0x0782, 0x0202,
0x0001
},
[AV_CRC_16_CCITT] = {
0x0000, 0x2110, 0x4220, 0x6330, 0x8440, 0xA550, 0xC660, 0xE770,
0x0881, 0x2991, 0x4AA1, 0x6BB1, 0x8CC1, 0xADD1, 0xCEE1, 0xEFF1,
0x3112, 0x1002, 0x7332, 0x5222, 0xB552, 0x9442, 0xF772, 0xD662,
0x3993, 0x1883, 0x7BB3, 0x5AA3, 0xBDD3, 0x9CC3, 0xFFF3, 0xDEE3,
0x6224, 0x4334, 0x2004, 0x0114, 0xE664, 0xC774, 0xA444, 0x8554,
0x6AA5, 0x4BB5, 0x2885, 0x0995, 0xEEE5, 0xCFF5, 0xACC5, 0x8DD5,
0x5336, 0x7226, 0x1116, 0x3006, 0xD776, 0xF666, 0x9556, 0xB446,
0x5BB7, 0x7AA7, 0x1997, 0x3887, 0xDFF7, 0xFEE7, 0x9DD7, 0xBCC7,
0xC448, 0xE558, 0x8668, 0xA778, 0x4008, 0x6118, 0x0228, 0x2338,
0xCCC9, 0xEDD9, 0x8EE9, 0xAFF9, 0x4889, 0x6999, 0x0AA9, 0x2BB9,
0xF55A, 0xD44A, 0xB77A, 0x966A, 0x711A, 0x500A, 0x333A, 0x122A,
0xFDDB, 0xDCCB, 0xBFFB, 0x9EEB, 0x799B, 0x588B, 0x3BBB, 0x1AAB,
0xA66C, 0x877C, 0xE44C, 0xC55C, 0x222C, 0x033C, 0x600C, 0x411C,
0xAEED, 0x8FFD, 0xECCD, 0xCDDD, 0x2AAD, 0x0BBD, 0x688D, 0x499D,
0x977E, 0xB66E, 0xD55E, 0xF44E, 0x133E, 0x322E, 0x511E, 0x700E,
0x9FFF, 0xBEEF, 0xDDDF, 0xFCCF, 0x1BBF, 0x3AAF, 0x599F, 0x788F,
0x8891, 0xA981, 0xCAB1, 0xEBA1, 0x0CD1, 0x2DC1, 0x4EF1, 0x6FE1,
0x8010, 0xA100, 0xC230, 0xE320, 0x0450, 0x2540, 0x4670, 0x6760,
0xB983, 0x9893, 0xFBA3, 0xDAB3, 0x3DC3, 0x1CD3, 0x7FE3, 0x5EF3,
0xB102, 0x9012, 0xF322, 0xD232, 0x3542, 0x1452, 0x7762, 0x5672,
0xEAB5, 0xCBA5, 0xA895, 0x8985, 0x6EF5, 0x4FE5, 0x2CD5, 0x0DC5,
0xE234, 0xC324, 0xA014, 0x8104, 0x6674, 0x4764, 0x2454, 0x0544,
0xDBA7, 0xFAB7, 0x9987, 0xB897, 0x5FE7, 0x7EF7, 0x1DC7, 0x3CD7,
0xD326, 0xF236, 0x9106, 0xB016, 0x5766, 0x7676, 0x1546, 0x3456,
0x4CD9, 0x6DC9, 0x0EF9, 0x2FE9, 0xC899, 0xE989, 0x8AB9, 0xABA9,
0x4458, 0x6548, 0x0678, 0x2768, 0xC018, 0xE108, 0x8238, 0xA328,
0x7DCB, 0x5CDB, 0x3FEB, 0x1EFB, 0xF98B, 0xD89B, 0xBBAB, 0x9ABB,
0x754A, 0x545A, 0x376A, 0x167A, 0xF10A, 0xD01A, 0xB32A, 0x923A,
0x2EFD, 0x0FED, 0x6CDD, 0x4DCD, 0xAABD, 0x8BAD, 0xE89D, 0xC98D,
0x267C, 0x076C, 0x645C, 0x454C, 0xA23C, 0x832C, 0xE01C, 0xC10C,
0x1FEF, 0x3EFF, 0x5DCF, 0x7CDF, 0x9BAF, 0xBABF, 0xD98F, 0xF89F,
0x176E, 0x367E, 0x554E, 0x745E, 0x932E, 0xB23E, 0xD10E, 0xF01E,
0x0001
},
[AV_CRC_24_IEEE] = {
0x000000, 0xFB4C86, 0x0DD58A, 0xF6990C, 0xE1E693, 0x1AAA15, 0xEC3319,
0x177F9F, 0x3981A1, 0xC2CD27, 0x34542B, 0xCF18AD, 0xD86732, 0x232BB4,
0xD5B2B8, 0x2EFE3E, 0x894EC5, 0x720243, 0x849B4F, 0x7FD7C9, 0x68A856,
0x93E4D0, 0x657DDC, 0x9E315A, 0xB0CF64, 0x4B83E2, 0xBD1AEE, 0x465668,
0x5129F7, 0xAA6571, 0x5CFC7D, 0xA7B0FB, 0xE9D10C, 0x129D8A, 0xE40486,
0x1F4800, 0x08379F, 0xF37B19, 0x05E215, 0xFEAE93, 0xD050AD, 0x2B1C2B,
0xDD8527, 0x26C9A1, 0x31B63E, 0xCAFAB8, 0x3C63B4, 0xC72F32, 0x609FC9,
0x9BD34F, 0x6D4A43, 0x9606C5, 0x81795A, 0x7A35DC, 0x8CACD0, 0x77E056,
0x591E68, 0xA252EE, 0x54CBE2, 0xAF8764, 0xB8F8FB, 0x43B47D, 0xB52D71,
0x4E61F7, 0xD2A319, 0x29EF9F, 0xDF7693, 0x243A15, 0x33458A, 0xC8090C,
0x3E9000, 0xC5DC86, 0xEB22B8, 0x106E3E, 0xE6F732, 0x1DBBB4, 0x0AC42B,
0xF188AD, 0x0711A1, 0xFC5D27, 0x5BEDDC, 0xA0A15A, 0x563856, 0xAD74D0,
0xBA0B4F, 0x4147C9, 0xB7DEC5, 0x4C9243, 0x626C7D, 0x9920FB, 0x6FB9F7,
0x94F571, 0x838AEE, 0x78C668, 0x8E5F64, 0x7513E2, 0x3B7215, 0xC03E93,
0x36A79F, 0xCDEB19, 0xDA9486, 0x21D800, 0xD7410C, 0x2C0D8A, 0x02F3B4,
0xF9BF32, 0x0F263E, 0xF46AB8, 0xE31527, 0x1859A1, 0xEEC0AD, 0x158C2B,
0xB23CD0, 0x497056, 0xBFE95A, 0x44A5DC, 0x53DA43, 0xA896C5, 0x5E0FC9,
0xA5434F, 0x8BBD71, 0x70F1F7, 0x8668FB, 0x7D247D, 0x6A5BE2, 0x911764,
0x678E68, 0x9CC2EE, 0xA44733, 0x5F0BB5, 0xA992B9, 0x52DE3F, 0x45A1A0,
0xBEED26, 0x48742A, 0xB338AC, 0x9DC692, 0x668A14, 0x901318, 0x6B5F9E,
0x7C2001, 0x876C87, 0x71F58B, 0x8AB90D, 0x2D09F6, 0xD64570, 0x20DC7C,
0xDB90FA, 0xCCEF65, 0x37A3E3, 0xC13AEF, 0x3A7669, 0x148857, 0xEFC4D1,
0x195DDD, 0xE2115B, 0xF56EC4, 0x0E2242, 0xF8BB4E, 0x03F7C8, 0x4D963F,
0xB6DAB9, 0x4043B5, 0xBB0F33, 0xAC70AC, 0x573C2A, 0xA1A526, 0x5AE9A0,
0x74179E, 0x8F5B18, 0x79C214, 0x828E92, 0x95F10D, 0x6EBD8B, 0x982487,
0x636801, 0xC4D8FA, 0x3F947C, 0xC90D70, 0x3241F6, 0x253E69, 0xDE72EF,
0x28EBE3, 0xD3A765, 0xFD595B, 0x0615DD, 0xF08CD1, 0x0BC057, 0x1CBFC8,
0xE7F34E, 0x116A42, 0xEA26C4, 0x76E42A, 0x8DA8AC, 0x7B31A0, 0x807D26,
0x9702B9, 0x6C4E3F, 0x9AD733, 0x619BB5, 0x4F658B, 0xB4290D, 0x42B001,
0xB9FC87, 0xAE8318, 0x55CF9E, 0xA35692, 0x581A14, 0xFFAAEF, 0x04E669,
0xF27F65, 0x0933E3, 0x1E4C7C, 0xE500FA, 0x1399F6, 0xE8D570, 0xC62B4E,
0x3D67C8, 0xCBFEC4, 0x30B242, 0x27CDDD, 0xDC815B, 0x2A1857, 0xD154D1,
0x9F3526, 0x6479A0, 0x92E0AC, 0x69AC2A, 0x7ED3B5, 0x859F33, 0x73063F,
0x884AB9, 0xA6B487, 0x5DF801, 0xAB610D, 0x502D8B, 0x475214, 0xBC1E92,
0x4A879E, 0xB1CB18, 0x167BE3, 0xED3765, 0x1BAE69, 0xE0E2EF, 0xF79D70,
0x0CD1F6, 0xFA48FA, 0x01047C, 0x2FFA42, 0xD4B6C4, 0x222FC8, 0xD9634E,
0xCE1CD1, 0x355057, 0xC3C95B, 0x3885DD, 0x000001,
},
[AV_CRC_32_IEEE] = {
0x00000000, 0xB71DC104, 0x6E3B8209, 0xD926430D, 0xDC760413, 0x6B6BC517,
0xB24D861A, 0x0550471E, 0xB8ED0826, 0x0FF0C922, 0xD6D68A2F, 0x61CB4B2B,
0x649B0C35, 0xD386CD31, 0x0AA08E3C, 0xBDBD4F38, 0x70DB114C, 0xC7C6D048,
0x1EE09345, 0xA9FD5241, 0xACAD155F, 0x1BB0D45B, 0xC2969756, 0x758B5652,
0xC836196A, 0x7F2BD86E, 0xA60D9B63, 0x11105A67, 0x14401D79, 0xA35DDC7D,
0x7A7B9F70, 0xCD665E74, 0xE0B62398, 0x57ABE29C, 0x8E8DA191, 0x39906095,
0x3CC0278B, 0x8BDDE68F, 0x52FBA582, 0xE5E66486, 0x585B2BBE, 0xEF46EABA,
0x3660A9B7, 0x817D68B3, 0x842D2FAD, 0x3330EEA9, 0xEA16ADA4, 0x5D0B6CA0,
0x906D32D4, 0x2770F3D0, 0xFE56B0DD, 0x494B71D9, 0x4C1B36C7, 0xFB06F7C3,
0x2220B4CE, 0x953D75CA, 0x28803AF2, 0x9F9DFBF6, 0x46BBB8FB, 0xF1A679FF,
0xF4F63EE1, 0x43EBFFE5, 0x9ACDBCE8, 0x2DD07DEC, 0x77708634, 0xC06D4730,
0x194B043D, 0xAE56C539, 0xAB068227, 0x1C1B4323, 0xC53D002E, 0x7220C12A,
0xCF9D8E12, 0x78804F16, 0xA1A60C1B, 0x16BBCD1F, 0x13EB8A01, 0xA4F64B05,
0x7DD00808, 0xCACDC90C, 0x07AB9778, 0xB0B6567C, 0x69901571, 0xDE8DD475,
0xDBDD936B, 0x6CC0526F, 0xB5E61162, 0x02FBD066, 0xBF469F5E, 0x085B5E5A,
0xD17D1D57, 0x6660DC53, 0x63309B4D, 0xD42D5A49, 0x0D0B1944, 0xBA16D840,
0x97C6A5AC, 0x20DB64A8, 0xF9FD27A5, 0x4EE0E6A1, 0x4BB0A1BF, 0xFCAD60BB,
0x258B23B6, 0x9296E2B2, 0x2F2BAD8A, 0x98366C8E, 0x41102F83, 0xF60DEE87,
0xF35DA999, 0x4440689D, 0x9D662B90, 0x2A7BEA94, 0xE71DB4E0, 0x500075E4,
0x892636E9, 0x3E3BF7ED, 0x3B6BB0F3, 0x8C7671F7, 0x555032FA, 0xE24DF3FE,
0x5FF0BCC6, 0xE8ED7DC2, 0x31CB3ECF, 0x86D6FFCB, 0x8386B8D5, 0x349B79D1,
0xEDBD3ADC, 0x5AA0FBD8, 0xEEE00C69, 0x59FDCD6D, 0x80DB8E60, 0x37C64F64,
0x3296087A, 0x858BC97E, 0x5CAD8A73, 0xEBB04B77, 0x560D044F, 0xE110C54B,
0x38368646, 0x8F2B4742, 0x8A7B005C, 0x3D66C158, 0xE4408255, 0x535D4351,
0x9E3B1D25, 0x2926DC21, 0xF0009F2C, 0x471D5E28, 0x424D1936, 0xF550D832,
0x2C769B3F, 0x9B6B5A3B, 0x26D61503, 0x91CBD407, 0x48ED970A, 0xFFF0560E,
0xFAA01110, 0x4DBDD014, 0x949B9319, 0x2386521D, 0x0E562FF1, 0xB94BEEF5,
0x606DADF8, 0xD7706CFC, 0xD2202BE2, 0x653DEAE6, 0xBC1BA9EB, 0x0B0668EF,
0xB6BB27D7, 0x01A6E6D3, 0xD880A5DE, 0x6F9D64DA, 0x6ACD23C4, 0xDDD0E2C0,
0x04F6A1CD, 0xB3EB60C9, 0x7E8D3EBD, 0xC990FFB9, 0x10B6BCB4, 0xA7AB7DB0,
0xA2FB3AAE, 0x15E6FBAA, 0xCCC0B8A7, 0x7BDD79A3, 0xC660369B, 0x717DF79F,
0xA85BB492, 0x1F467596, 0x1A163288, 0xAD0BF38C, 0x742DB081, 0xC3307185,
0x99908A5D, 0x2E8D4B59, 0xF7AB0854, 0x40B6C950, 0x45E68E4E, 0xF2FB4F4A,
0x2BDD0C47, 0x9CC0CD43, 0x217D827B, 0x9660437F, 0x4F460072, 0xF85BC176,
0xFD0B8668, 0x4A16476C, 0x93300461, 0x242DC565, 0xE94B9B11, 0x5E565A15,
0x87701918, 0x306DD81C, 0x353D9F02, 0x82205E06, 0x5B061D0B, 0xEC1BDC0F,
0x51A69337, 0xE6BB5233, 0x3F9D113E, 0x8880D03A, 0x8DD09724, 0x3ACD5620,
0xE3EB152D, 0x54F6D429, 0x7926A9C5, 0xCE3B68C1, 0x171D2BCC, 0xA000EAC8,
0xA550ADD6, 0x124D6CD2, 0xCB6B2FDF, 0x7C76EEDB, 0xC1CBA1E3, 0x76D660E7,
0xAFF023EA, 0x18EDE2EE, 0x1DBDA5F0, 0xAAA064F4, 0x738627F9, 0xC49BE6FD,
0x09FDB889, 0xBEE0798D, 0x67C63A80, 0xD0DBFB84, 0xD58BBC9A, 0x62967D9E,
0xBBB03E93, 0x0CADFF97, 0xB110B0AF, 0x060D71AB, 0xDF2B32A6, 0x6836F3A2,
0x6D66B4BC, 0xDA7B75B8, 0x035D36B5, 0xB440F7B1, 0x00000001
},
[AV_CRC_32_IEEE_LE] = {
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F,
0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2,
0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423,
0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106,
0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7,
0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA,
0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84,
0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E,
0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55,
0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28,
0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F,
0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69,
0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693,
0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D, 0x00000001
},
[AV_CRC_16_ANSI_LE] = {
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040,
0x0001
},
};
#else
#if CONFIG_SMALL
#define CRC_TABLE_SIZE 257
#else
#define CRC_TABLE_SIZE 1024
#endif
static AVCRC av_crc_table[AV_CRC_MAX][CRC_TABLE_SIZE];
#define DECLARE_CRC_INIT_TABLE_ONCE(id, le, bits, poly) \
static AVOnce id ## _once_control = AV_ONCE_INIT; \
static void id ## _init_table_once(void) \
{ \
av_assert0(av_crc_init(av_crc_table[id], le, bits, poly, sizeof(av_crc_table[id])) >= 0); \
}
#define CRC_INIT_TABLE_ONCE(id) ff_thread_once(&id ## _once_control, id ## _init_table_once)
DECLARE_CRC_INIT_TABLE_ONCE(AV_CRC_8_ATM, 0, 8, 0x07)
DECLARE_CRC_INIT_TABLE_ONCE(AV_CRC_8_EBU, 0, 8, 0x1D)
DECLARE_CRC_INIT_TABLE_ONCE(AV_CRC_16_ANSI, 0, 16, 0x8005)
DECLARE_CRC_INIT_TABLE_ONCE(AV_CRC_16_CCITT, 0, 16, 0x1021)
DECLARE_CRC_INIT_TABLE_ONCE(AV_CRC_24_IEEE, 0, 24, 0x864CFB)
DECLARE_CRC_INIT_TABLE_ONCE(AV_CRC_32_IEEE, 0, 32, 0x04C11DB7)
DECLARE_CRC_INIT_TABLE_ONCE(AV_CRC_32_IEEE_LE, 1, 32, 0xEDB88320)
DECLARE_CRC_INIT_TABLE_ONCE(AV_CRC_16_ANSI_LE, 1, 16, 0xA001)
#endif
int av_crc_init(AVCRC *ctx, int le, int bits, uint32_t poly, int ctx_size)
{
unsigned i, j;
uint32_t c;
if (bits < 8 || bits > 32 || poly >= (1LL << bits))
return AVERROR(EINVAL);
if (ctx_size != sizeof(AVCRC) * 257 && ctx_size != sizeof(AVCRC) * 1024)
return AVERROR(EINVAL);
for (i = 0; i < 256; i++) {
if (le) {
for (c = i, j = 0; j < 8; j++)
c = (c >> 1) ^ (poly & (-(c & 1)));
ctx[i] = c;
} else {
for (c = i << 24, j = 0; j < 8; j++)
c = (c << 1) ^ ((poly << (32 - bits)) & (((int32_t) c) >> 31));
ctx[i] = av_bswap32(c);
}
}
ctx[256] = 1;
#if !CONFIG_SMALL
if (ctx_size >= sizeof(AVCRC) * 1024)
for (i = 0; i < 256; i++)
for (j = 0; j < 3; j++)
ctx[256 * (j + 1) + i] =
(ctx[256 * j + i] >> 8) ^ ctx[ctx[256 * j + i] & 0xFF];
#endif
return 0;
}
const AVCRC *av_crc_get_table(AVCRCId crc_id)
{
#if !CONFIG_HARDCODED_TABLES
switch (crc_id) {
case AV_CRC_8_ATM: CRC_INIT_TABLE_ONCE(AV_CRC_8_ATM); break;
case AV_CRC_8_EBU: CRC_INIT_TABLE_ONCE(AV_CRC_8_EBU); break;
case AV_CRC_16_ANSI: CRC_INIT_TABLE_ONCE(AV_CRC_16_ANSI); break;
case AV_CRC_16_CCITT: CRC_INIT_TABLE_ONCE(AV_CRC_16_CCITT); break;
case AV_CRC_24_IEEE: CRC_INIT_TABLE_ONCE(AV_CRC_24_IEEE); break;
case AV_CRC_32_IEEE: CRC_INIT_TABLE_ONCE(AV_CRC_32_IEEE); break;
case AV_CRC_32_IEEE_LE: CRC_INIT_TABLE_ONCE(AV_CRC_32_IEEE_LE); break;
case AV_CRC_16_ANSI_LE: CRC_INIT_TABLE_ONCE(AV_CRC_16_ANSI_LE); break;
default: av_assert0(0);
}
#endif
return av_crc_table[crc_id];
}
uint32_t av_crc(const AVCRC *ctx, uint32_t crc,
const uint8_t *buffer, size_t length)
{
const uint8_t *end = buffer + length;
#if !CONFIG_SMALL
if (!ctx[256]) {
while (((intptr_t) buffer & 3) && buffer < end)
crc = ctx[((uint8_t) crc) ^ *buffer++] ^ (crc >> 8);
while (buffer < end - 3) {
crc ^= av_le2ne32(*(const uint32_t *) buffer); buffer += 4;
crc = ctx[3 * 256 + ( crc & 0xFF)] ^
ctx[2 * 256 + ((crc >> 8 ) & 0xFF)] ^
ctx[1 * 256 + ((crc >> 16) & 0xFF)] ^
ctx[0 * 256 + ((crc >> 24) )];
}
}
#endif
while (buffer < end)
crc = ctx[((uint8_t) crc) ^ *buffer++] ^ (crc >> 8);
return crc;
}

493
dreamcast/pvrtex/dither.cpp Normal file
View File

@ -0,0 +1,493 @@
//From https://bisqwit.iki.fi/story/howto/dither/jy/
//~ #include <gd.h>
#include <stdio.h>
#include <math.h>
#include <algorithm> /* For std::sort() */
#include <vector>
#include <map> /* For associative container, std::map<> */
////////////////////////////////////////////////////
#if 0
#include <utility> // for std::pair
#include "alloc/FSBAllocator.hh"
/* kd-tree implementation translated to C++
* from java implementation in VideoMosaic
* at http://www.intelegance.net/video/videomosaic.shtml.
*/
template<typename V, unsigned K = 3>
class KDTree {
public:
struct KDPoint {
double coord[K];
KDPoint() { }
KDPoint(double a,double b,double c) {
coord[0] = a;
coord[1] = b;
coord[2] = c;
}
KDPoint(double v[K]) {
for(unsigned n=0; n<K; ++n)
coord[n] = v[n];
}
bool operator==(const KDPoint& b) const {
for(unsigned n=0; n<K; ++n)
if(coord[n] != b.coord[n]) return false;
return true;
}
double sqrdist(const KDPoint& b) const {
double result = 0;
for(unsigned n=0; n<K; ++n) {
double diff = coord[n] - b.coord[n];
result += diff*diff;
}
return result;
}
};
private:
struct KDRect {
KDPoint min, max;
KDPoint bound(const KDPoint& t) const {
KDPoint p;
for(unsigned i=0; i<K; ++i)
if(t.coord[i] <= min.coord[i])
p.coord[i] = min.coord[i];
else if(t.coord[i] >= max.coord[i])
p.coord[i] = max.coord[i];
else
p.coord[i] = t.coord[i];
return p;
}
void MakeInfinite() {
for(unsigned i=0; i<K; ++i) {
min.coord[i] = -1e99;
max.coord[i] = 1e99;
}
}
};
struct KDNode {
KDPoint k;
V v;
KDNode *left, *right;
public:
KDNode() : k(),v(),left(0),right(0) { }
KDNode(const KDPoint& kk, const V& vv) : k(kk), v(vv), left(0), right(0) { }
virtual ~KDNode() {
delete (left);
delete (right);
}
static KDNode* ins( const KDPoint& key, const V& val,
KDNode*& t, int lev) {
if(!t)
return (t = new KDNode(key, val));
else if(key == t->k)
return 0; /* key duplicate */
else if(key.coord[lev] > t->k.coord[lev])
return ins(key, val, t->right, (lev+1)%K);
else
return ins(key, val, t->left, (lev+1)%K);
}
struct Nearest {
const KDNode* kd;
double dist_sqd;
};
// Method Nearest Neighbor from Andrew Moore's thesis. Numbered
// comments are direct quotes from there. Step "SDL" is added to
// make the algorithm work correctly.
static void nnbr(const KDNode* kd, const KDPoint& target,
KDRect& hr, // in-param and temporary; not an out-param.
int lev,
Nearest& nearest) {
// 1. if kd is empty then set dist-sqd to infinity and exit.
if (!kd) return;
// 2. s := split field of kd
int s = lev % K;
// 3. pivot := dom-elt field of kd
const KDPoint& pivot = kd->k;
double pivot_to_target = pivot.sqrdist(target);
// 4. Cut hr into to sub-hyperrectangles left-hr and right-hr.
// The cut plane is through pivot and perpendicular to the s
// dimension.
KDRect& left_hr = hr; // optimize by not cloning
KDRect right_hr = hr;
left_hr.max.coord[s] = pivot.coord[s];
right_hr.min.coord[s] = pivot.coord[s];
// 5. target-in-left := target_s <= pivot_s
bool target_in_left = target.coord[s] < pivot.coord[s];
const KDNode* nearer_kd;
const KDNode* further_kd;
KDRect nearer_hr;
KDRect further_hr;
// 6. if target-in-left then nearer is left, further is right
if (target_in_left) {
nearer_kd = kd->left;
nearer_hr = left_hr;
further_kd = kd->right;
further_hr = right_hr;
}
// 7. if not target-in-left then nearer is right, further is left
else {
nearer_kd = kd->right;
nearer_hr = right_hr;
further_kd = kd->left;
further_hr = left_hr;
}
// 8. Recursively call Nearest Neighbor with parameters
// (nearer-kd, target, nearer-hr, max-dist-sqd), storing the
// results in nearest and dist-sqd
nnbr(nearer_kd, target, nearer_hr, lev + 1, nearest);
// 10. A nearer point could only lie in further-kd if there were some
// part of further-hr within distance sqrt(max-dist-sqd) of
// target. If this is the case then
const KDPoint closest = further_hr.bound(target);
if (closest.sqrdist(target) < nearest.dist_sqd) {
// 10.1 if (pivot-target)^2 < dist-sqd then
if (pivot_to_target < nearest.dist_sqd) {
// 10.1.1 nearest := (pivot, range-elt field of kd)
nearest.kd = kd;
// 10.1.2 dist-sqd = (pivot-target)^2
nearest.dist_sqd = pivot_to_target;
}
// 10.2 Recursively call Nearest Neighbor with parameters
// (further-kd, target, further-hr, max-dist_sqd)
nnbr(further_kd, target, further_hr, lev + 1, nearest);
}
// SDL: otherwise, current point is nearest
else if (pivot_to_target < nearest.dist_sqd) {
nearest.kd = kd;
nearest.dist_sqd = pivot_to_target;
}
}
private:
void operator=(const KDNode&);
public:
KDNode(const KDNode& b)
: k(b.k), v(b.v),
left( b.left ? new KDNode(*b.left) : 0),
right( b.right ? new KDNode(*b.right) : 0 ) { }
};
private:
KDNode* m_root;
public:
KDTree() : m_root(0) { }
virtual ~KDTree() {
delete (m_root);
}
bool insert(const KDPoint& key, const V& val) {
return KDNode::ins(key, val, m_root, 0);
}
const std::pair<V,double> nearest(const KDPoint& key) const {
KDRect hr;
hr.MakeInfinite();
typename KDNode::Nearest nn;
nn.kd = 0;
nn.dist_sqd = 1e99;
KDNode::nnbr(m_root, key, hr, 0, nn);
if(!nn.kd) return std::pair<V,double> ( V(), 1e99 );
return std::pair<V,double> ( nn.kd->v, nn.dist_sqd);
}
public:
KDTree& operator=(const KDTree&b) {
if(this != &b) {
if(m_root) delete (m_root);
m_root = b.m_root ? new KDNode(*b.m_root) : 0;
m_count = b.m_count;
}
return *this;
}
KDTree(const KDTree& b)
: m_root( b.m_root ? new KDNode(*b.m_root) : 0 ),
m_count( b.m_count ) { }
};
#endif
////////////////////////////////////////////////////
#define COMPARE_RGB 1
/* 8x8 threshold map */
static const unsigned char map[8*8] = {
0,48,12,60, 3,51,15,63,
32,16,44,28,35,19,47,31,
8,56, 4,52,11,59, 7,55,
40,24,36,20,43,27,39,23,
2,50,14,62, 1,49,13,61,
34,18,46,30,33,17,45,29,
10,58, 6,54, 9,57, 5,53,
42,26,38,22,41,25,37,21
};
static const double Gamma = 2.2; // Gamma correction we use.
double GammaCorrect(double v) {
return pow(v, Gamma);
}
double GammaUncorrect(double v) {
return pow(v, 1.0 / Gamma);
}
/* CIE C illuminant */
static const double illum[3*3] = {
0.488718, 0.176204, 0.000000,
0.310680, 0.812985, 0.0102048,
0.200602, 0.0108109, 0.989795
};
struct LabItem { // CIE L*a*b* color value with C and h added.
double L,a,b,C,h;
LabItem() { }
LabItem(double R,double G,double B) {
Set(R,G,B);
}
void Set(double R,double G,double B) {
const double* const i = illum;
double X = i[0]*R + i[3]*G + i[6]*B, x = X / (i[0] + i[1] + i[2]);
double Y = i[1]*R + i[4]*G + i[7]*B, y = Y / (i[3] + i[4] + i[5]);
double Z = i[2]*R + i[5]*G + i[8]*B, z = Z / (i[6] + i[7] + i[8]);
const double threshold1 = (6*6*6.0)/(29*29*29.0);
const double threshold2 = (29*29.0)/(6*6*3.0);
double x1 = (x > threshold1) ? pow(x, 1.0/3.0) : (threshold2*x)+(4/29.0);
double y1 = (y > threshold1) ? pow(y, 1.0/3.0) : (threshold2*y)+(4/29.0);
double z1 = (z > threshold1) ? pow(z, 1.0/3.0) : (threshold2*z)+(4/29.0);
L = (29*4)*y1 - (4*4);
a = (500*(x1-y1) );
b = (200*(y1-z1) );
C = sqrt(a*a + b+b);
h = atan2(b, a);
}
LabItem(unsigned rgb) {
Set(rgb);
}
void Set(unsigned rgb) {
Set( (rgb>>16)/255.0, ((rgb>>8)&0xFF)/255.0, (rgb&0xFF)/255.0 );
}
};
/* From the paper "The CIEDE2000 Color-Difference Formula: Implementation Notes, */
/* Supplementary Test Data, and Mathematical Observations", by */
/* Gaurav Sharma, Wencheng Wu and Edul N. Dalal, */
/* Color Res. Appl., vol. 30, no. 1, pp. 21-30, Feb. 2005. */
/* Return the CIEDE2000 Delta E color difference measure squared, for two Lab values */
double ColorCompare(const LabItem& lab1, const LabItem& lab2) {
#define RAD2DEG(xx) (180.0/M_PI * (xx))
#define DEG2RAD(xx) (M_PI/180.0 * (xx))
/* Compute Cromanance and Hue angles */
double C1,C2, h1,h2;
{
double Cab = 0.5 * (lab1.C + lab2.C);
double Cab7 = pow(Cab,7.0);
double G = 0.5 * (1.0 - sqrt(Cab7/(Cab7 + 6103515625.0)));
double a1 = (1.0 + G) * lab1.a;
double a2 = (1.0 + G) * lab2.a;
C1 = sqrt(a1 * a1 + lab1.b * lab1.b);
C2 = sqrt(a2 * a2 + lab2.b * lab2.b);
if (C1 < 1e-9)
h1 = 0.0;
else {
h1 = RAD2DEG(atan2(lab1.b, a1));
if (h1 < 0.0)
h1 += 360.0;
}
if (C2 < 1e-9)
h2 = 0.0;
else {
h2 = RAD2DEG(atan2(lab2.b, a2));
if (h2 < 0.0)
h2 += 360.0;
}
}
/* Compute delta L, C and H */
double dL = lab2.L - lab1.L, dC = C2 - C1, dH;
{
double dh;
if (C1 < 1e-9 || C2 < 1e-9) {
dh = 0.0;
} else {
dh = h2 - h1;
/**/ if (dh > 180.0) dh -= 360.0;
else if (dh < -180.0) dh += 360.0;
}
dH = 2.0 * sqrt(C1 * C2) * sin(DEG2RAD(0.5 * dh));
}
double h;
double L = 0.5 * (lab1.L + lab2.L);
double C = 0.5 * (C1 + C2);
if (C1 < 1e-9 || C2 < 1e-9) {
h = h1 + h2;
} else {
h = h1 + h2;
if (fabs(h1 - h2) > 180.0) {
/**/ if (h < 360.0) h += 360.0;
else if (h >= 360.0) h -= 360.0;
}
h *= 0.5;
}
double T = 1.0
- 0.17 * cos(DEG2RAD(h - 30.0))
+ 0.24 * cos(DEG2RAD(2.0 * h))
+ 0.32 * cos(DEG2RAD(3.0 * h + 6.0))
- 0.2 * cos(DEG2RAD(4.0 * h - 63.0));
double hh = (h - 275.0)/25.0;
double ddeg = 30.0 * exp(-hh * hh);
double C7 = pow(C,7.0);
double RC = 2.0 * sqrt(C7/(C7 + 6103515625.0));
double L50sq = (L - 50.0) * (L - 50.0);
double SL = 1.0 + (0.015 * L50sq) / sqrt(20.0 + L50sq);
double SC = 1.0 + 0.045 * C;
double SH = 1.0 + 0.015 * C * T;
double RT = -sin(DEG2RAD(2 * ddeg)) * RC;
double dLsq = dL/SL, dCsq = dC/SC, dHsq = dH/SH;
return dLsq*dLsq + dCsq*dCsq + dHsq*dHsq + RT*dCsq*dHsq;
#undef RAD2DEG
#undef DEG2RAD
}
double ColorCompare(int r1,int g1,int b1, int r2,int g2,int b2) {
double luma1 = (r1*299 + g1*587 + b1*114) / (255.0*1000);
double luma2 = (r2*299 + g2*587 + b2*114) / (255.0*1000);
double lumadiff = luma1-luma2;
double diffR = (r1-r2)/255.0, diffG = (g1-g2)/255.0, diffB = (b1-b2)/255.0;
return (diffR*diffR*0.299 + diffG*diffG*0.587 + diffB*diffB*0.114)*0.75
+ lumadiff*lumadiff;
}
/* Palette */
static const unsigned palettesize = 16;
static const unsigned pal[palettesize] = {
0x080000,0x201A0B,0x432817,0x492910, 0x234309,0x5D4F1E,0x9C6B20,0xA9220F,
0x2B347C,0x2B7409,0xD0CA40,0xE8A077, 0x6A94AB,0xD5C4B3,0xFCE76E,0xFCFAE2
};
/* Luminance for each palette entry, to be initialized as soon as the program begins */
static unsigned luma[palettesize];
static LabItem meta[palettesize];
static double pal_g[palettesize][3]; // Gamma-corrected palette entry
inline bool PaletteCompareLuma(unsigned index1, unsigned index2) {
return luma[index1] < luma[index2];
}
typedef std::vector<unsigned> MixingPlan;
MixingPlan DeviseBestMixingPlan(unsigned color, size_t limit) {
// Input color in RGB
int input_rgb[3] = { (int)((color>>16)&0xFF),
(int)((color>>8)&0xFF),
(int)(color&0xFF)
};
// Input color in CIE L*a*b*
LabItem input(color);
// Tally so far (gamma-corrected)
double so_far[3] = { 0,0,0 };
MixingPlan result;
while(result.size() < limit) {
unsigned chosen_amount = 1;
unsigned chosen = 0;
const unsigned max_test_count = result.empty() ? 1 : result.size();
double least_penalty = -1;
for(unsigned index=0; index<palettesize; ++index) {
//~ const unsigned color = pal[index];
double sum[3] = { so_far[0], so_far[1], so_far[2] };
double add[3] = { pal_g[index][0], pal_g[index][1], pal_g[index][2] };
for(unsigned p=1; p<=max_test_count; p*=2) {
for(unsigned c=0; c<3; ++c) sum[c] += add[c];
for(unsigned c=0; c<3; ++c) add[c] += add[c];
double t = result.size() + p;
double test[3] = { GammaUncorrect(sum[0]/t),
GammaUncorrect(sum[1]/t),
GammaUncorrect(sum[2]/t)
};
#if COMPARE_RGB
double penalty = ColorCompare(
input_rgb[0],input_rgb[1],input_rgb[2],
test[0]*255, test[1]*255, test[2]*255);
#else
LabItem test_lab( test[0], test[1], test[2] );
double penalty = ColorCompare(test_lab, input);
#endif
if(penalty < least_penalty || least_penalty < 0) {
least_penalty = penalty;
chosen = index;
chosen_amount = p;
}
}
}
// Append "chosen_amount" times "chosen" to the color list
result.resize(result.size() + chosen_amount, chosen);
for(unsigned c=0; c<3; ++c)
so_far[c] += pal_g[chosen][c] * chosen_amount;
}
// Sort the colors according to luminance
std::sort(result.begin(), result.end(), PaletteCompareLuma);
return result;
}
#if 0
int main(int argc, char**argv) {
FILE* fp = fopen(argv[1], "rb");
gdImagePtr srcim = gdImageCreateFromPng(fp);
fclose(fp);
unsigned w = gdImageSX(srcim), h = gdImageSY(srcim);
gdImagePtr im = gdImageCreate(w, h);
for(unsigned c=0; c<palettesize; ++c) {
unsigned r = pal[c]>>16, g = (pal[c]>>8) & 0xFF, b = pal[c] & 0xFF;
gdImageColorAllocate(im, r,g,b);
luma[c] = r*299 + g*587 + b*114;
meta[c].Set(pal[c]);
pal_g[c][0] = GammaCorrect(r/255.0);
pal_g[c][1] = GammaCorrect(g/255.0);
pal_g[c][2] = GammaCorrect(b/255.0);
}
#pragma omp parallel for
for(unsigned y=0; y<h; ++y)
for(unsigned x=0; x<w; ++x) {
unsigned color = gdImageGetTrueColorPixel(srcim, x, y);
unsigned map_value = map[(x & 7) + ((y & 7) << 3)];
MixingPlan plan = DeviseBestMixingPlan(color, 16);
map_value = map_value * plan.size() / 64;
gdImageSetPixel(im, x,y, plan[ map_value ]);
}
fp = fopen(argv[2], "wb");
gdImagePng(im, fp);
fclose(fp);
gdImageDestroy(im);
gdImageDestroy(srcim);
return 0;
}
#endif

530
dreamcast/pvrtex/elbg.c Normal file
View File

@ -0,0 +1,530 @@
/*
* Copyright (C) 2007 Vitor Sessak <vitor1001@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
* Codebook Generator using the ELBG algorithm
*/
#include <string.h>
#include "libavutil/avassert.h"
#include "libavutil/common.h"
#include "libavutil/lfg.h"
#include "elbg.h"
#define DELTA_ERR_MAX 0.1 ///< Precision of the ELBG algorithm (as percentage error)
/**
* In the ELBG jargon, a cell is the set of points that are closest to a
* codebook entry. Not to be confused with a RoQ Video cell. */
typedef struct cell_s {
int index;
struct cell_s *next;
} cell;
/**
* ELBG internal data
*/
typedef struct ELBGContext {
int error;
int dim;
int num_cb;
int *codebook;
cell **cells;
int *utility;
int *utility_inc;
int *nearest_cb;
int *points;
int *temp_points;
int *size_part;
AVLFG *rand_state;
int *scratchbuf;
cell *cell_buffer;
/* Sizes for the buffers above. Pointers without such a field
* are not allocated by us and only valid for the duration
* of a single call to avpriv_elbg_do(). */
unsigned utility_allocated;
unsigned utility_inc_allocated;
unsigned size_part_allocated;
unsigned cells_allocated;
unsigned scratchbuf_allocated;
unsigned cell_buffer_allocated;
unsigned temp_points_allocated;
} ELBGContext;
static inline int distance_limited(int *a, int *b, int dim, int limit)
{
int i, dist=0;
for (i=0; i<dim; i++) {
int64_t distance = a[i] - b[i];
distance *= distance;
if (dist >= limit - distance)
return limit;
dist += distance;
}
return dist;
}
static inline void vect_division(int *res, int *vect, int div, int dim)
{
int i;
if (div > 1)
for (i=0; i<dim; i++)
res[i] = ROUNDED_DIV(vect[i],div);
else if (res != vect)
memcpy(res, vect, dim*sizeof(int));
}
static int eval_error_cell(ELBGContext *elbg, int *centroid, cell *cells)
{
int error=0;
for (; cells; cells=cells->next) {
int distance = distance_limited(centroid, elbg->points + cells->index*elbg->dim, elbg->dim, INT_MAX);
if (error >= INT_MAX - distance)
return INT_MAX;
error += distance;
}
return error;
}
static int get_closest_codebook(ELBGContext *elbg, int index)
{
int pick = 0;
for (int i = 0, diff_min = INT_MAX; i < elbg->num_cb; i++)
if (i != index) {
int diff;
diff = distance_limited(elbg->codebook + i*elbg->dim, elbg->codebook + index*elbg->dim, elbg->dim, diff_min);
if (diff < diff_min) {
pick = i;
diff_min = diff;
}
}
return pick;
}
static int get_high_utility_cell(ELBGContext *elbg)
{
int i=0;
/* Using linear search, do binary if it ever turns to be speed critical */
uint64_t r;
if (elbg->utility_inc[elbg->num_cb - 1] < INT_MAX) {
r = av_lfg_get(elbg->rand_state) % (unsigned int)elbg->utility_inc[elbg->num_cb - 1] + 1;
} else {
r = av_lfg_get(elbg->rand_state);
r = (av_lfg_get(elbg->rand_state) + (r<<32)) % elbg->utility_inc[elbg->num_cb - 1] + 1;
}
while (elbg->utility_inc[i] < r) {
i++;
}
av_assert2(elbg->cells[i]);
return i;
}
/**
* Implementation of the simple LBG algorithm for just two codebooks
*/
static int simple_lbg(ELBGContext *elbg,
int dim,
int *centroid[3],
int newutility[3],
int *points,
cell *cells)
{
int i, idx;
int numpoints[2] = {0,0};
int *newcentroid[2] = {
elbg->scratchbuf + 3*dim,
elbg->scratchbuf + 4*dim
};
cell *tempcell;
memset(newcentroid[0], 0, 2 * dim * sizeof(*newcentroid[0]));
newutility[0] =
newutility[1] = 0;
for (tempcell = cells; tempcell; tempcell=tempcell->next) {
idx = distance_limited(centroid[0], points + tempcell->index*dim, dim, INT_MAX)>=
distance_limited(centroid[1], points + tempcell->index*dim, dim, INT_MAX);
numpoints[idx]++;
for (i=0; i<dim; i++)
newcentroid[idx][i] += points[tempcell->index*dim + i];
}
vect_division(centroid[0], newcentroid[0], numpoints[0], dim);
vect_division(centroid[1], newcentroid[1], numpoints[1], dim);
for (tempcell = cells; tempcell; tempcell=tempcell->next) {
int dist[2] = {distance_limited(centroid[0], points + tempcell->index*dim, dim, INT_MAX),
distance_limited(centroid[1], points + tempcell->index*dim, dim, INT_MAX)};
int idx = dist[0] > dist[1];
if (newutility[idx] >= INT_MAX - dist[idx])
newutility[idx] = INT_MAX;
else
newutility[idx] += dist[idx];
}
return (newutility[0] >= INT_MAX - newutility[1]) ? INT_MAX : newutility[0] + newutility[1];
}
static void get_new_centroids(ELBGContext *elbg, int huc, int *newcentroid_i,
int *newcentroid_p)
{
cell *tempcell;
int *min = newcentroid_i;
int *max = newcentroid_p;
int i;
for (i=0; i< elbg->dim; i++) {
min[i]=INT_MAX;
max[i]=0;
}
for (tempcell = elbg->cells[huc]; tempcell; tempcell = tempcell->next)
for(i=0; i<elbg->dim; i++) {
min[i]=FFMIN(min[i], elbg->points[tempcell->index*elbg->dim + i]);
max[i]=FFMAX(max[i], elbg->points[tempcell->index*elbg->dim + i]);
}
for (i=0; i<elbg->dim; i++) {
int ni = min[i] + (max[i] - min[i])/3;
int np = min[i] + (2*(max[i] - min[i]))/3;
newcentroid_i[i] = ni;
newcentroid_p[i] = np;
}
}
/**
* Add the points in the low utility cell to its closest cell. Split the high
* utility cell, putting the separated points in the (now empty) low utility
* cell.
*
* @param elbg Internal elbg data
* @param indexes {luc, huc, cluc}
* @param newcentroid A vector with the position of the new centroids
*/
static void shift_codebook(ELBGContext *elbg, int *indexes,
int *newcentroid[3])
{
cell *tempdata;
cell **pp = &elbg->cells[indexes[2]];
while(*pp)
pp= &(*pp)->next;
*pp = elbg->cells[indexes[0]];
elbg->cells[indexes[0]] = NULL;
tempdata = elbg->cells[indexes[1]];
elbg->cells[indexes[1]] = NULL;
while(tempdata) {
cell *tempcell2 = tempdata->next;
int idx = distance_limited(elbg->points + tempdata->index*elbg->dim,
newcentroid[0], elbg->dim, INT_MAX) >
distance_limited(elbg->points + tempdata->index*elbg->dim,
newcentroid[1], elbg->dim, INT_MAX);
tempdata->next = elbg->cells[indexes[idx]];
elbg->cells[indexes[idx]] = tempdata;
tempdata = tempcell2;
}
}
static void evaluate_utility_inc(ELBGContext *elbg)
{
int64_t inc=0;
for (int i = 0; i < elbg->num_cb; i++) {
if (elbg->num_cb * (int64_t)elbg->utility[i] > elbg->error)
inc += elbg->utility[i];
elbg->utility_inc[i] = FFMIN(inc, INT_MAX);
}
}
static void update_utility_and_n_cb(ELBGContext *elbg, int idx, int newutility)
{
cell *tempcell;
elbg->utility[idx] = newutility;
for (tempcell=elbg->cells[idx]; tempcell; tempcell=tempcell->next)
elbg->nearest_cb[tempcell->index] = idx;
}
/**
* Evaluate if a shift lower the error. If it does, call shift_codebooks
* and update elbg->error, elbg->utility and elbg->nearest_cb.
*
* @param elbg Internal elbg data
* @param idx {luc (low utility cell, huc (high utility cell), cluc (closest cell to low utility cell)}
*/
static void try_shift_candidate(ELBGContext *elbg, int idx[3])
{
int j, k, cont=0, tmp;
int64_t olderror=0, newerror;
int newutility[3];
int *newcentroid[3] = {
elbg->scratchbuf,
elbg->scratchbuf + elbg->dim,
elbg->scratchbuf + 2*elbg->dim
};
cell *tempcell;
for (j=0; j<3; j++)
olderror += elbg->utility[idx[j]];
memset(newcentroid[2], 0, elbg->dim*sizeof(int));
for (k=0; k<2; k++)
for (tempcell=elbg->cells[idx[2*k]]; tempcell; tempcell=tempcell->next) {
cont++;
for (j=0; j<elbg->dim; j++)
newcentroid[2][j] += elbg->points[tempcell->index*elbg->dim + j];
}
vect_division(newcentroid[2], newcentroid[2], cont, elbg->dim);
get_new_centroids(elbg, idx[1], newcentroid[0], newcentroid[1]);
newutility[2] = eval_error_cell(elbg, newcentroid[2], elbg->cells[idx[0]]);
tmp = eval_error_cell(elbg, newcentroid[2], elbg->cells[idx[2]]);
newutility[2] = (tmp >= INT_MAX - newutility[2]) ? INT_MAX : newutility[2] + tmp;
newerror = newutility[2];
tmp = simple_lbg(elbg, elbg->dim, newcentroid, newutility, elbg->points,
elbg->cells[idx[1]]);
if (tmp >= INT_MAX - newerror)
newerror = INT_MAX;
else
newerror += tmp;
if (olderror > newerror) {
shift_codebook(elbg, idx, newcentroid);
elbg->error += newerror - olderror;
for (j=0; j<3; j++)
update_utility_and_n_cb(elbg, idx[j], newutility[j]);
evaluate_utility_inc(elbg);
}
}
/**
* Implementation of the ELBG block
*/
static void do_shiftings(ELBGContext *elbg)
{
int idx[3];
evaluate_utility_inc(elbg);
for (idx[0]=0; idx[0] < elbg->num_cb; idx[0]++)
if (elbg->num_cb * (int64_t)elbg->utility[idx[0]] < elbg->error) {
if (elbg->utility_inc[elbg->num_cb - 1] == 0)
return;
idx[1] = get_high_utility_cell(elbg);
idx[2] = get_closest_codebook(elbg, idx[0]);
if (idx[1] != idx[0] && idx[1] != idx[2])
try_shift_candidate(elbg, idx);
}
}
static void do_elbg(ELBGContext *av_restrict elbg, int *points, int numpoints,
int max_steps)
{
int *const size_part = elbg->size_part;
int i, j, steps = 0;
int best_idx = 0;
int last_error;
elbg->error = INT_MAX;
elbg->points = points;
do {
cell *free_cells = elbg->cell_buffer;
last_error = elbg->error;
steps++;
memset(elbg->utility, 0, elbg->num_cb * sizeof(*elbg->utility));
memset(elbg->cells, 0, elbg->num_cb * sizeof(*elbg->cells));
elbg->error = 0;
/* This loop evaluate the actual Voronoi partition. It is the most
costly part of the algorithm. */
for (i=0; i < numpoints; i++) {
int best_dist = distance_limited(elbg->points + i * elbg->dim,
elbg->codebook + best_idx * elbg->dim,
elbg->dim, INT_MAX);
for (int k = 0; k < elbg->num_cb; k++) {
int dist = distance_limited(elbg->points + i * elbg->dim,
elbg->codebook + k * elbg->dim,
elbg->dim, best_dist);
if (dist < best_dist) {
best_dist = dist;
best_idx = k;
}
}
elbg->nearest_cb[i] = best_idx;
elbg->error = (elbg->error >= INT_MAX - best_dist) ? INT_MAX : elbg->error + best_dist;
elbg->utility[elbg->nearest_cb[i]] = (elbg->utility[elbg->nearest_cb[i]] >= INT_MAX - best_dist) ?
INT_MAX : elbg->utility[elbg->nearest_cb[i]] + best_dist;
free_cells->index = i;
free_cells->next = elbg->cells[elbg->nearest_cb[i]];
elbg->cells[elbg->nearest_cb[i]] = free_cells;
free_cells++;
}
do_shiftings(elbg);
memset(size_part, 0, elbg->num_cb * sizeof(*size_part));
memset(elbg->codebook, 0, elbg->num_cb * elbg->dim * sizeof(*elbg->codebook));
for (i=0; i < numpoints; i++) {
size_part[elbg->nearest_cb[i]]++;
for (j=0; j < elbg->dim; j++)
elbg->codebook[elbg->nearest_cb[i]*elbg->dim + j] +=
elbg->points[i*elbg->dim + j];
}
for (int i = 0; i < elbg->num_cb; i++)
vect_division(elbg->codebook + i*elbg->dim,
elbg->codebook + i*elbg->dim, size_part[i], elbg->dim);
} while(((last_error - elbg->error) > DELTA_ERR_MAX*elbg->error) &&
(steps < max_steps));
}
#define BIG_PRIME 433494437LL
/**
* Initialize the codebook vector for the elbg algorithm.
* If numpoints <= 24 * num_cb this function fills codebook with random numbers.
* If not, it calls do_elbg for a (smaller) random sample of the points in
* points.
*/
static void init_elbg(ELBGContext *av_restrict elbg, int *points, int *temp_points,
int numpoints, int max_steps)
{
int dim = elbg->dim;
if (numpoints > 24LL * elbg->num_cb) {
/* ELBG is very costly for a big number of points. So if we have a lot
of them, get a good initial codebook to save on iterations */
for (int i = 0; i < numpoints / 8; i++) {
int k = (i*BIG_PRIME) % numpoints;
memcpy(temp_points + i*dim, points + k*dim, dim * sizeof(*temp_points));
}
/* If anything is changed in the recursion parameters,
* the allocated size of temp_points will also need to be updated. */
init_elbg(elbg, temp_points, temp_points + numpoints / 8 * dim,
numpoints / 8, 2 * max_steps);
do_elbg(elbg, temp_points, numpoints / 8, 2 * max_steps);
} else // If not, initialize the codebook with random positions
for (int i = 0; i < elbg->num_cb; i++)
memcpy(elbg->codebook + i * dim, points + ((i*BIG_PRIME)%numpoints)*dim,
dim * sizeof(*elbg->codebook));
}
int avpriv_elbg_do(ELBGContext **elbgp, int *points, int dim, int numpoints,
int *codebook, int num_cb, int max_steps,
int *closest_cb, AVLFG *rand_state, uintptr_t flags)
{
ELBGContext *const av_restrict elbg = *elbgp ? *elbgp : av_mallocz(sizeof(*elbg));
if (!elbg)
return AVERROR(ENOMEM);
*elbgp = elbg;
elbg->nearest_cb = closest_cb;
elbg->rand_state = rand_state;
elbg->codebook = codebook;
elbg->num_cb = num_cb;
elbg->dim = dim;
#define ALLOCATE_IF_NECESSARY(field, new_elements, multiplicator) \
if (elbg->field ## _allocated < new_elements) { \
av_freep(&elbg->field); \
elbg->field = av_malloc_array(new_elements, \
multiplicator * sizeof(*elbg->field)); \
if (!elbg->field) { \
elbg->field ## _allocated = 0; \
return AVERROR(ENOMEM); \
} \
elbg->field ## _allocated = new_elements; \
}
/* Allocating the buffers for do_elbg() here once relies
* on their size being always the same even when do_elbg()
* is called from init_elbg(). It also relies on do_elbg()
* never calling itself recursively. */
ALLOCATE_IF_NECESSARY(cells, num_cb, 1)
ALLOCATE_IF_NECESSARY(utility, num_cb, 1)
ALLOCATE_IF_NECESSARY(utility_inc, num_cb, 1)
ALLOCATE_IF_NECESSARY(size_part, num_cb, 1)
ALLOCATE_IF_NECESSARY(cell_buffer, numpoints, 1)
ALLOCATE_IF_NECESSARY(scratchbuf, dim, 5)
if (numpoints > 24LL * elbg->num_cb) {
/* The first step in the recursion in init_elbg() needs a buffer with
* (numpoints / 8) * dim elements; the next step needs numpoints / 8 / 8
* * dim elements etc. The geometric series leads to an upper bound of
* numpoints / 8 * 8 / 7 * dim elements. */
uint64_t prod = dim * (uint64_t)(numpoints / 7U);
if (prod > INT_MAX)
return AVERROR(ERANGE);
ALLOCATE_IF_NECESSARY(temp_points, prod, 1)
}
init_elbg(elbg, points, elbg->temp_points, numpoints, max_steps);
do_elbg (elbg, points, numpoints, max_steps);
return 0;
}
av_cold void avpriv_elbg_free(ELBGContext **elbgp)
{
ELBGContext *elbg = *elbgp;
if (!elbg)
return;
av_freep(&elbg->size_part);
av_freep(&elbg->utility);
av_freep(&elbg->cell_buffer);
av_freep(&elbg->cells);
av_freep(&elbg->utility_inc);
av_freep(&elbg->scratchbuf);
av_freep(&elbg->temp_points);
av_freep(elbgp);
}

59
dreamcast/pvrtex/elbg.h Normal file
View File

@ -0,0 +1,59 @@
/*
* Copyright (C) 2007 Vitor Sessak <vitor1001@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
*/
#ifndef AVCODEC_ELBG_H
#define AVCODEC_ELBG_H
#include <stdint.h>
#include "libavutil/lfg.h"
#define av_restrict restrict
struct ELBGContext;
/**
* Implementation of the Enhanced LBG Algorithm
* Based on the paper "Neural Networks 14:1219-1237" that can be found in
* http://citeseer.ist.psu.edu/patan01enhanced.html .
*
* @param ctx A pointer to a pointer to an already allocated ELBGContext
* or a pointer to NULL. In the latter case, this function
* will allocate an ELBGContext and put a pointer to it in `*ctx`.
* @param points Input points.
* @param dim Dimension of the points.
* @param numpoints Num of points in **points.
* @param codebook Pointer to the output codebook. Must be allocated.
* @param num_cb Number of points in the codebook.
* @param num_steps The maximum number of steps. One step is already a good compromise between time and quality.
* @param closest_cb Return the closest codebook to each point. Must be allocated.
* @param rand_state A random number generator state. Should be already initialized by av_lfg_init().
* @param flags Currently unused; must be set to 0.
* @return < 0 in case of error, 0 otherwise
*/
int avpriv_elbg_do(struct ELBGContext **ctx, int *points, int dim,
int numpoints, int *codebook, int num_cb, int num_steps,
int *closest_cb, AVLFG *rand_state, uintptr_t flags);
/**
* Free an ELBGContext and reset the pointer to it.
*/
void avpriv_elbg_free(struct ELBGContext **ctx);
#endif /* AVCODEC_ELBG_H */

View File

@ -0,0 +1,74 @@
#include <assert.h>
#include <string.h>
#include "file_common.h"
#include "pvr_texture_encoder.h"
void CheckedFwrite(const void *data, size_t size, FILE *f) {
int writeamt = fwrite(data, 1, size, f);
if (writeamt != size) {
perror("");
ErrorExit("write error, wanted to write %i, but only wrote %i\n", size, writeamt);
}
}
void WriteFourCC(const char *fourcc, FILE *f) {
assert(strlen(fourcc) == 4);
CheckedFwrite(fourcc, 4, f);
}
void Write8(unsigned int val, FILE *f) {
char vb[1] = {val};
CheckedFwrite(vb, 1, f);
}
void Write32LE(unsigned int val, FILE *f) {
char vb[4] = {val, val >> 8, val >> 16, val >> 24};
CheckedFwrite(vb, 4, f);
}
void Write16LE(unsigned int val, FILE *f) {
char vb[2] = {val, val >> 8};
CheckedFwrite(vb, 2, f);
}
void WritePadZero(size_t len, FILE *f) {
static char paddingarea[64] = {0};
assert(f);
assert(len < sizeof(paddingarea));
CheckedFwrite(&paddingarea, len, f);
}
void WritePvrTexEncoder(const PvrTexEncoder *pte, FILE *f, ptewSmallVQType svq, int mip_skip) {
assert(pte);
assert(pte->pvr_tex);
assert(f);
unsigned texsize = CalcTextureSize(pte->w, pte->h, pte->pixel_format, pteHasMips(pte), pteIsCompressed(pte), 0);
if (pteIsCompressed(pte)) {
assert(pte->pvr_codebook);
//Write CB
unsigned cbsize = pte->codebook_size * PVR_CODEBOOK_ENTRY_SIZE_BYTES;
if (svq == PTEW_NO_SMALL_VQ)
cbsize = PVR_CODEBOOK_SIZE_BYTES;
pteLog(LOG_DEBUG, "Writing %u bytes for codebook\n", (unsigned)cbsize);
CheckedFwrite(pte->pvr_codebook + pte->pvr_idx_offset * PVR_CODEBOOK_ENTRY_SIZE_BYTES, cbsize, f);
}
if (!pteIsCompressed(pte) && pteHasMips(pte)) {
CheckedFwrite(pte->pvr_tex + mip_skip, texsize-mip_skip, f);
} else {
CheckedFwrite(pte->pvr_tex, texsize, f);
}
}
int FileSize(const char *fname) {
assert(fname);
FILE *f = fopen(fname, "r");
if (f == NULL)
return -1;
fseek(f, 0, SEEK_END);
int size = ftell(f);
fclose(f);
return size;
}

View File

@ -0,0 +1,22 @@
#pragma once
#include <stdio.h>
#include "pvr_texture_encoder.h"
typedef enum {
PTEW_NO_SMALL_VQ,
PTEW_FILE_PVR_SMALL_VQ,
PTEW_FILE_DCTEX_SMALL_VQ,
} ptewSmallVQType;
void WriteFourCC(const char *fourcc, FILE *f);
void Write32LE(unsigned int val, FILE *f);
void Write16LE(unsigned int val, FILE *f);
void Write8(unsigned int val, FILE *f);
//If mip_pad_override is less than zero, add normal padding to uncompressed mipmapped textures
//If mip_pad_override is >= 0, use mip_pad_override as the number of padding bytes for uncompressed mipmapped textures
void WritePvrTexEncoder(const PvrTexEncoder *td, FILE *f, ptewSmallVQType svq, int mip_pad_override);
void WritePadZero(size_t len, FILE *f);
//Returns size of file, returns -1 if file does not exist
int FileSize(const char *fname);

View File

@ -0,0 +1,88 @@
#include <stdio.h>
#include <assert.h>
#include "pvr_texture_encoder.h"
#include "file_common.h"
#include "file_dctex.h"
static int convert_size(int size) {
if (size > 512)
return 7;
else if (size > 256)
return 6;
else if (size > 128)
return 5;
else if (size > 64)
return 4;
else if (size > 32)
return 3;
else if (size > 16)
return 2;
else if (size > 8)
return 1;
else
return 0;
}
void fDtWrite(const PvrTexEncoder *pte, const char *outfname) {
assert(pte);
FILE *f = fopen(outfname, "w");
assert(f);
unsigned textype = 0;
textype |= pteHasMips(pte) << FDT_MIPMAP_SHIFT;
textype |= pteIsCompressed(pte) << FDT_VQ_SHIFT;
textype |= pte->pixel_format << FDT_PIXEL_FORMAT_SHIFT;
textype |= !pte->raw_is_twiddled << FDT_NOT_TWIDDLED_SHIFT;
//If the width is a power of two, we don't need the stride bit set
textype |= (pteIsStrided(pte) && !IsPow2(pte->w)) << FDT_STRIDE_SHIFT;
textype |= ((pte->w / 32) & FDT_STRIDE_VAL_MASK) << FDT_STRIDE_VAL_SHIFT;
textype |= !IsPow2(pte->h) << FDT_PARTIAL_SHIFT;
textype |= convert_size(pte->w) << FDT_WIDTH_SHIFT;
textype |= convert_size(pte->h) << FDT_HEIGHT_SHIFT;
//Include size of header
unsigned origsize = 32 + CalcTextureSize(pte->w, pte->h, pte->pixel_format, pteHasMips(pte), pteIsCompressed(pte), pte->codebook_size * 8);
unsigned size = ROUND_UP_POW2(origsize, 32);
unsigned paddingamt = size - origsize;
pteLog(LOG_DEBUG, "File size: %u orig + %u pad = %u total\n", origsize, paddingamt, size);
WriteFourCC("DcTx", f);
Write32LE(size, f);
Write8(0, f); //Version
Write8(0, f); //Header size is (32 bytes / 32 - 1) = 0
Write8(pteIsCompressed(pte) ? pte->codebook_size - 1 : 0, f);
Write8(pteIsPalettized(pte) ? pte->palette_size - 1 : 0, f);
Write16LE(pte->w, f);
Write16LE(pte->h, f);
Write32LE(textype, f);
Write32LE(0, f);
Write32LE(0, f);
Write32LE(0, f);
WritePvrTexEncoder(pte, f, PTEW_FILE_DCTEX_SMALL_VQ, 0);
//Pad to 32 bytes
WritePadZero(paddingamt, f);
fclose(f);
//Validate resulting file
unsigned resultsize = FileSize(outfname);
ErrorExitOn(resultsize != size, "Size of file written for \"%s\" was incorrect. Expected file to be %u bytes, but result was %u bytes.\n", outfname, size, resultsize);
f = fopen(outfname, "r");
void *readbuff = malloc(size);
if (fread(readbuff, size, 1, f) == 1) {
if (!fDtValidateHeader(readbuff)) {
pteLog(LOG_WARNING, "**Error validating output for .DT**\n");
}
} else {
pteLog(LOG_WARNING, "**Error reading file during validation check for .DT output**\n");
}
fclose(f);
free(readbuff);
}

View File

@ -0,0 +1,560 @@
#pragma once
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
//These match up with the PVR hardware texture format bits
typedef enum fdtPixelFormat {
FDT_FMT_ARGB1555,
FDT_FMT_RGB565,
FDT_FMT_ARGB4444,
FDT_FMT_YUV,
FDT_FMT_NORMAL,
FDT_FMT_PALETTE_4BPP,
FDT_FMT_PALETTE_8BPP,
} fdtPixelFormat;
#define FDT_PVR_SIZE_MASK 0x0000003F
#define FDT_PVR_MODE_MASK 0xFC000000
#define FDT_PVR_MODE_PAL_MASK 0xFFE00000
#define FDT_CODEBOOK_MAX_SIZE_BYTES 2048
#define FDT_MIPMAP_SHIFT 31
#define FDT_MIPMAP_MASK 1
#define FDT_VQ_SHIFT 30
#define FDT_VQ_MASK 1
#define FDT_PIXEL_FORMAT_MASK 0x7
#define FDT_PIXEL_FORMAT_SHIFT 27
#define FDT_NOT_TWIDDLED_SHIFT 26
#define FDT_NOT_TWIDDLED_MASK 1
#define FDT_STRIDE_SHIFT 25
#define FDT_STRIDE_MASK 1
#define FDT_PIXEL_FORMAT_MASK 0x7
#define FDT_PARTIAL_SHIFT 11
#define FDT_PARTIAL_MASK 1
#define FDT_STRIDE_VAL_SHIFT 6
#define FDT_STRIDE_VAL_MASK 0x1F
#define FDT_WIDTH_SHIFT 3
#define FDT_WIDTH_MASK 0x7
#define FDT_HEIGHT_SHIFT 0
#define FDT_HEIGHT_MASK 0x7
typedef struct {
/*
All data is little endian (same as the Dreamcast's SH-4)
Header is 32 bytes large
*/
/*
Four character code to identify file format.
Always equal to "DcTx"
*/
char fourcc[4];
/*
Size of file, including header. This is different to IFF, which does not include the size of the fourcc and size fields.
Size is always rounded up to 32 bytes
*/
uint32_t chunk_size;
/*
File format version of this texture.
Currently, the only version is 0
*/
uint8_t version;
/*
Size of the header in 32-byte units, minus one. This is how far from the start of the header the texture data starts.
The header can be 32 bytes to 8KB large.
A header_size of 0 means the texture data starts 32 bytes after the start of the header, a size of 3 means 128 bytes...
This allows for backwards compatible changes to the size of the header, or adding additional user data.
*/
uint8_t header_size;
/*
Values range from 0-255, mapping to 1-256. To get the real number of codebook entries or colors,
add one to this value. (e.g. if colors_used == 15, then there are 16 different colors, if,
colors_used == 2, then 2 colors are used)
The value of codebook_size is undefined if not compressed, and should not be relied upon.
colors_used is similarly undefined if pixel format is not 8BPP or 4BPP.
The functions fDtGetColorsUsed and fDtGetColorsUsed can be used to preform the checks and offseting.
They return 0 if not palletized or compressed
*/
uint8_t codebook_size, colors_used;
/*
Height and width of the texture in pixels.
This might not match up with the sizes in pvr_size.
Stride textures will have the true width here, but the size in pvr_size
will be rounded up to the next power of two.
It's possible to have non-twiddled textures with a height that is not a power of two,
the size in pvr_size will be rounded up to the next power of two. The PVR does not
have a texture format that supports this, but if you don't display any texels from
outside the real area, it will still work.
*/
uint16_t width_pixels, height_pixels;
/*
The bottom 6 bits match up with the third long in the PVR's triangle/quad command,
and the top 10 bits match up with the fourth long.
Bit 31:
Mipmapped (0 = no mipmaps, 1 = has mipmaps)
Bit 30:
VQ compression (0 = not compressed, 1 = is compressed)
Bits 29-27:
Pixel format
0: ARGB1555
1: RGB565
2: ARGB4444
3: YUV422
4: Spherical normal
5: 4-bit palette
6: 8-bit palette
For pixel formats other than palettized:
Bit 26:
Not twiddled (0 = twiddled, 1 = not twiddled)
Bit 25:
Strided (0 = width is power of two, 1 = width is not power of two)
For palettized pixel formats:
Bit 26-21:
Palette hint (This is currently always 0, but could be used in the future)
Palettized textures are always assumed to be twiddled and never strided by the hardware
Bit 11:
Partial texture (0 = not partial, 1 = partial)
A partial texture is a texture that does not have all data to fill the PVRs view of the texture.
This saves video RAM, but can result in the PVR reading garbage if you try to display texels from outside
the defined range. The undefined data in a partial texture is always at the end of the texture, never the
middle or start. (Small codebook VQ textures would techincally qualify as a type of partical texture with
some of the data at the start of a texture missing, they are not considered to be partial for the purposes
of this bit.)
For nontwiddled data, a partial texture results in the bottom rows of the texture being undefined. For
twiddled textures, this results in the topmost mip level having data in the right half being undefined.
For example, a 640x480 texture can at best be seen by the PVR as a 640x512 texture. Trying to display
data from below the 480th row will result in the PVR reading undefined data.
For a twiddled, mipmapped texture, a partial texture might be missing the bottom right corner or
or entire right half the highest mip level, or other shapes. This type is not currently supported by the
converter.
Bits 10-6:
Stride value. This is the width of the texture divided by 32.
Only valid on texture will stride bit set.
Place this value in the bottom 5 bits of the PVR register PVR_TEXTURE_MODULO (at address 0xA05F80E4).
Only one stride value can be used per frame.
Bits 5-3:
Texture width
Value Pixels
0 8
1 16
2 32
3 64
4 128
5 256
6 512
7 1024
For stride textures, this will be set to the next size larger than the stride value.
For example, a 640 pixel wide texture will have a width of 7 (1024).
Bits 2-0:
Texture height
Value Pixels
0 8
1 16
2 32
3 64
4 128
5 256
6 512
7 1024
For textures with a height that is not a power of two, the value here will be rounded up.
For example, a 480 pixel high texture will have a height of 6 (512).
*/
uint32_t pvr_type;
/*
Pad header out to 32 bytes
DMA requires 32 byte alignment.
Padding might be used in future versions as an identifer or hash of the texture,
to help track identical textures, or store other user data like material properties.
*/
uint32_t pad1;
uint32_t pad2;
uint32_t pad3;
//Texture data follows...
} fDtHeader;
/*
Returns true if the fourcc matches
*/
static inline bool fDtFourccMatches(const fDtHeader *tex) {
const int *fourcc = (const int *)&tex->fourcc;
return *fourcc == 0x78546344; //'DxTc'
/* return tex->fourcc[0] == 'D' &&
tex->fourcc[1] == 'c' &&
tex->fourcc[2] == 'T' &&
tex->fourcc[3] == 'x'; */
}
/*
Returns version of texture
*/
static inline int fDtGetVersion(const fDtHeader *tex) {
return tex->version;
}
/*
Returns size of file, including header and texture data. Will always be a multiple of 32 (assuming valid texture).
*/
static inline size_t fDtGetTotalSize(const fDtHeader *tex) {
return tex->chunk_size;
}
/*
Returns the size of the header in bytes
*/
static inline size_t fDtGetHeaderSize(const fDtHeader *tex) {
return (tex->header_size+1) * 32;
}
/*
Returns size of texture data. Does not include header. Will always be a multiple of 32 (assuming valid texture).
*/
static inline size_t fDtGetTextureSize(const fDtHeader *tex) {
return fDtGetTotalSize(tex) - fDtGetHeaderSize(tex);
}
/*
Returns pointer to end of texture (byte after final byte of texture)
*/
static inline void * fDtGetNextChunk(const fDtHeader *tex) {
return (void*)tex + fDtGetTotalSize(tex);
}
/*
Returns true width of texture in pixels.
The value that needs to be passed to the PVR might be different than this. Use
fDtGetPvrWidthBits to get the value that should be given to the PVR to correctly
use the texture.
*/
static inline unsigned fDtGetWidth(const fDtHeader *tex) {
return tex->width_pixels;
}
/*
Returns true height of texture in pixels.
The value that needs to be passed to the PVR might be different than this. Use
fDtGetPvrHeightBits to get the value that should be given to the PVR to correctly
use the texture.
*/
static inline unsigned fDtGetHeight(const fDtHeader *tex) {
return tex->height_pixels;
}
/*
Returns the PVR pixel format of the texture. You can pass this directly to the PVR as the texture format.
*/
static inline unsigned fDtGetPixelFormat(const fDtHeader *tex) {
return (tex->pvr_type >> FDT_PIXEL_FORMAT_SHIFT) & FDT_PIXEL_FORMAT_MASK;
}
/*
Returns true if the texture is palettized (i.e. texture is 4BPP or 8BPP).
*/
static inline int fDtIsPalettized(const fDtHeader *tex) {
unsigned fmt = fDtGetPixelFormat(tex);
return fmt == FDT_FMT_PALETTE_8BPP || fmt == FDT_FMT_PALETTE_4BPP;
}
/*
Returns the stride value of the texture.
Result is undefined if texture is not strided.
*/
static inline unsigned int fDtGetStride(const fDtHeader *tex) {
return ((tex->pvr_type >> FDT_STRIDE_VAL_SHIFT) & FDT_STRIDE_VAL_MASK);
}
/*
Returns true if the texture is a partial texture (see format description for expination of partial textures),
and returns false if the texture is complete.
*/
static inline int fDtIsPartial(const fDtHeader *tex) {
return (tex->pvr_type & (1<<FDT_PARTIAL_SHIFT)) != 0;
}
/*
Returns true if the texture is a stride texture (not power of two width), or false if the
texture has a power of two width.
*/
static inline int fDtIsStrided(const fDtHeader *tex) {
return ((tex->pvr_type & (1<<FDT_STRIDE_SHIFT)) != 0) && !fDtIsPalettized(tex);
}
/*
Returns true if the texture is twiddled, or false if the texture is not.
*/
static inline int fDtIsTwiddled(const fDtHeader *tex) {
return fDtIsPalettized(tex) || ((tex->pvr_type & (1<<26)) == 0);
}
/*
Returns true if the texture is compressed, or false if it is not.
*/
static inline int fDtIsCompressed(const fDtHeader *tex) {
return (tex->pvr_type & (1<<FDT_VQ_SHIFT)) != 0;
}
/*
Returns true if the texture is mipmapped, or false if it is not.
*/
static inline int fDtIsMipmapped(const fDtHeader *tex) {
return ((int32_t)tex->pvr_type) < 0;
}
/*
Returns the width value to written to the TA command to use for the texture.
*/
static inline unsigned fDtGetPvrWidthBits(const fDtHeader *tex) {
return (tex->pvr_type >> FDT_WIDTH_SHIFT) & FDT_WIDTH_MASK;
}
/*
Returns the height value to written to the TA command to use for the texture.
*/
static inline unsigned fDtGetPvrHeightBits(const fDtHeader *tex) {
return (tex->pvr_type >> FDT_HEIGHT_SHIFT) & FDT_HEIGHT_MASK;
}
/*
Returns how wide/high the PVR thinks the texture is in pixels.
This is needed to figure out the correct U value for strided textures.
You can use fDtGetUWidth or fDtGetVHeight to find the correct value for
the edge of the texture.
*/
static inline unsigned fDtGetPvrWidth(const fDtHeader *tex) {
return 1 << (fDtGetPvrWidthBits(tex) + 3);
}
static inline unsigned fDtGetPvrHeight(const fDtHeader *tex) {
return 1 << (fDtGetPvrHeightBits(tex) + 3);
}
/*
Returns U value for right edge of texture.
This can be used to map the entire valid area of the texture to a polygon, with
the top left UV coord being (0, 0), and the bottom right UV coord
being (fDtGetUWidth(tex), fDtGetVHeight(tex))
*/
static inline float fDtGetUWidth(const fDtHeader *tex) {
return (float)fDtGetWidth(tex) / fDtGetPvrWidth(tex);
}
/*
Returns V value for bottom edge of texture
See fDtGetUWidth description
*/
static inline float fDtGetVHeight(const fDtHeader *tex) {
return (float)fDtGetHeight(tex) / fDtGetPvrHeight(tex);
}
/*
Returns number of colors used by palettized texture.
Returns 0 if texture does not use a palette.
*/
static inline unsigned fDtGetColorsUsed(const fDtHeader *tex) {
return fDtIsPalettized(tex) ? tex->colors_used+1 : 0;
}
/*
Returns size of codebook for compressed texture in bytes.
Returns 0 if texture is not compressed.
*/
static inline unsigned fDtGetCodebookSizeBytes(const fDtHeader *tex) {
return fDtIsCompressed(tex) ? (tex->codebook_size+1) * 8 : 0;
}
/*
Returns pointer to pixel data
*/
static inline void * fDtGetPvrTexData(const fDtHeader *tex) {
return (void*)tex + fDtGetHeaderSize(tex);
}
/*
Preforms sanity checking on header to guess if it is valid.
Checks fourcc, version, size, and texture format.
Returns true if checks passes, or false if any failed.
This is too large to be made static inline like the other functions in here,
but seperating out one function to a .c file would be a pain, so it's
just static. The compiler can get rid of it if it's not used.
The unused attribute is to disable any warnings if this is not used.
*/
static bool __attribute__((unused)) fDtValidateHeader(const fDtHeader *tex) {
bool valid = true;
//Check fourcc matches
valid &= fDtFourccMatches(tex);
//Currently, only version is 0. There will probably not be more than 50 versions,
//so anything more than that is suspicious
valid &= fDtGetVersion(tex) < 50;
//Size should be multiple of 32
valid &= (fDtGetTotalSize(tex) % 32) == 0;
//Check texture dimensions
valid &= fDtGetWidth(tex) >= 8;
valid &= fDtGetWidth(tex) <= 1024;
valid &= fDtGetPvrWidth(tex) >= fDtGetWidth(tex);
valid &= fDtGetHeight(tex) > 0;
valid &= fDtGetHeight(tex) <= 1024;
valid &= fDtGetPvrHeight(tex) >= fDtGetHeight(tex);
//Check size is expected value
unsigned size = fDtGetWidth(tex)*fDtGetHeight(tex)*2;
//Calculate size of texture
if (fDtIsMipmapped(tex))
size = size * 4/3 + 6;
if (fDtGetPixelFormat(tex) == FDT_FMT_PALETTE_8BPP)
size /= 2;
else if (fDtGetPixelFormat(tex) == FDT_FMT_PALETTE_4BPP)
size /= 4;
if (fDtIsCompressed(tex))
size = (size+7)/8 + fDtGetCodebookSizeBytes(tex);
//Round up to 32 bytes
size = (size + 31) & ~0x1f;
//Add header size
size += fDtGetHeaderSize(tex);
valid &= fDtGetTotalSize(tex) == size;
//Check valid pixel format
valid &= fDtGetPixelFormat(tex) <= FDT_FMT_PALETTE_8BPP;
//If strided texture has height is not equal to PVR height, must be partial
if (fDtIsStrided(tex) && (fDtGetHeight(tex) != fDtGetPvrHeight(tex)))
valid &= fDtIsPartial(tex);
return valid;
}
#ifdef _arch_dreamcast
/*
Small codebook compressed textures require an adjustment to the texture pointer for the PVR to
render it correctly. This function will preform the adjustment.
For compressed textures, returns adjusted pointer to video RAM
For uncompressed textures, returns pointer unchanged.
This does not modify the texture or it's allocation in any way, so when freeing the texture, be
sure to use the original, unadjusted pvr_ptr_t pointer.
*/
static inline pvr_ptr_t fDtAdjustPVRPointer(const fDtHeader * texheader, pvr_ptr_t pvr) {
if (fDtIsCompressed(texheader)) {
return pvr - FDT_CODEBOOK_MAX_SIZE_BYTES + fDtGetCodebookSizeBytes(texheader);
} else {
return pvr;
}
}
/*
Set PVR hardware register for stride to value required for this texture
You want to avoid calling this while rendering, but KOS doesn't really provide a way to do that...
A work around would be to only set the stride value on a frame wher no strided texture is being rendered.
*/
static inline void fDtSetPvrStride(const fDtHeader *tex) {
if (fDtIsStrided(tex))
PVR_SET(PVR_TEXTURE_MODULO, fDtGetStride(tex));
}
/*
These set the texture for a KOS compiled polygon header struct.
The format of the texture is taken from tex, and the actual address of the texture is
set by video_ram_addr.
*/
static inline void fDtSetTAParameters(pvr_poly_hdr_t *dst, const fDtHeader *tex, pvr_ptr_t video_ram_addr) {
dst->mode2 = (dst->mode2 & ~FDT_PVR_SIZE_MASK) | (tex->pvr_type & FDT_PVR_SIZE_MASK);
dst->mode3 = (tex->pvr_type & FDT_PVR_MODE_PAL_MASK);
dst->mode3 |= 0x1ffffff & ((unsigned)fDtAdjustPVRPointer(tex, video_ram_addr) >> 3);
}
static inline void fDtSetTAParametersIC(pvr_poly_ic_hdr_t *dst, const fDtHeader *tex, pvr_ptr_t video_ram_addr) {
dst->mode2 = (dst->mode2 & ~FDT_PVR_SIZE_MASK) | (tex->pvr_type & FDT_PVR_SIZE_MASK);
dst->mode3 = (tex->pvr_type & FDT_PVR_MODE_PAL_MASK);
dst->mode3 |= 0x1ffffff & ((unsigned)fDtAdjustPVRPointer(tex, video_ram_addr) >> 3);
}
static inline void fDtSetTAParametersSprite(pvr_sprite_hdr_t *dst, const fDtHeader *tex, pvr_ptr_t video_ram_addr) {
dst->mode2 = (dst->mode2 & ~FDT_PVR_SIZE_MASK) | (tex->pvr_type & FDT_PVR_SIZE_MASK);
dst->mode3 = (tex->pvr_type & FDT_PVR_MODE_PAL_MASK);
dst->mode3 |= 0x1ffffff & ((unsigned)fDtAdjustPVRPointer(tex, video_ram_addr) >> 3);
}
static inline void fDtSetTAParametersMod(pvr_poly_mod_hdr_t *dst, const fDtHeader *tex, pvr_ptr_t video_ram_addr, int param) {
if (param == 0) {
dst->mode2_0 = (dst->mode2_0 & ~FDT_PVR_SIZE_MASK) | (tex->pvr_type & FDT_PVR_SIZE_MASK);
dst->mode3_0 = (tex->pvr_type & FDT_PVR_MODE_PAL_MASK);
dst->mode3_0 |= 0x1ffffff & ((unsigned)fDtAdjustPVRPointer(tex, video_ram_addr) >> 3);
} else {
dst->mode2_1 = (dst->mode2_1 & ~FDT_PVR_SIZE_MASK) | (tex->pvr_type & FDT_PVR_SIZE_MASK);
dst->mode3_1 = (tex->pvr_type & FDT_PVR_MODE_PAL_MASK);
dst->mode3_1 |= 0x1ffffff & ((unsigned)fDtAdjustPVRPointer(tex, video_ram_addr));
}
}
#ifdef PVR_CXT_GUARD
/*
This is for my pvr_context library. pvr_cxt.h must be included beforehand.
*/
static inline void fDtSetPvrContextMod(pvr_context_submodes *dst, const fDtHeader *tex, pvr_ptr_t video_ram_addr) {
dst->mode2 = (dst->mode2 & ~FDT_PVR_SIZE_MASK) | (tex->pvr_type & FDT_PVR_SIZE_MASK);
dst->tex = (tex->pvr_type & FDT_PVR_MODE_PAL_MASK);
pc_set_texture_address_mod(dst, fDtAdjustPVRPointer(tex, video_ram_addr));
}
static inline void fDtSetPvrContext(pvr_context *dst, const fDtHeader *tex, pvr_ptr_t video_ram_addr) {
pc_set_textured(dst, 1);
fDtSetPvrContextMod(pc_no_mod(dst), tex, video_ram_addr);
}
#endif
#endif

101
dreamcast/pvrtex/file_pvr.c Normal file
View File

@ -0,0 +1,101 @@
#include <stdio.h>
#include <assert.h>
#include "pvr_texture_encoder.h"
#include "file_common.h"
#include "file_pvr.h"
int fPvrSmallVQCodebookSize(int texsize_pixels, int mip) {
// 16x16 = 256 bytes Small VQ
if (texsize_pixels <= 32)
return 24;
// 16x32 or 32x16 = 512 bytes Small VQ
if(texsize_pixels == 48)
return 48;
// 32x32 = 512 bytes Small VQ
if(texsize_pixels == 64)
return 32;
// 64x32 or 32x64 = 1024 bytes Small VQ
if(texsize_pixels == 96)
return 64;
// 64xx64 = 1536 bytes Small VQ
if(texsize_pixels == 128)
return 64;
// 128x64 or 64x128 = 3584 bytes Small VQ
if(texsize_pixels == 192)
return 192;
return 256;
}
void fPvrWrite(const PvrTexEncoder *pte, const char *outfname) {
assert(pte);
assert(pte->pvr_tex);
assert(outfname);
FILE *f = fopen(outfname, "wb");
assert(f);
//Write header
unsigned chunksize = 16;
unsigned pvrfmt = FILE_PVR_SQUARE;
if (pteIsCompressed(pte)) {
pvrfmt = FILE_PVR_VQ;
unsigned cb_size = 2048;
unsigned int idxcnt = pte->w * pte->h / 4;
if (pteHasMips(pte))
idxcnt = idxcnt * 4/3 + 1;
if (pte->auto_small_vq) {
//We only generate real small VQ textures when small_vq is set
pvrfmt = FILE_PVR_SMALL_VQ;
cb_size = pte->codebook_size * 8;
}
if (pteIsPalettized(pte))
ErrorExit(".PVR format does not support compressed palettized textures\n");
// JP - Rectangle VQ certainly does work on real hardware
//if (pte->w != pte->h)
// ErrorExit(".PVR format does not support non-square compressed textures\n");
chunksize += idxcnt+cb_size;
} else {
chunksize += CalcTextureSize(pte->w, pte->h, pte->pixel_format, pteHasMips(pte), 0, 0);
if (pte->pixel_format == PTE_PALETTE_8B) {
pvrfmt = FILE_PVR_8BPP;
} else if (pte->pixel_format == PTE_PALETTE_4B) {
pvrfmt = FILE_PVR_4BPP;
}
//.PVR does not store first 4 padding bytes of uncompressed mipmapped texture
if (pteHasMips(pte))
chunksize -= 4;
if (pte->w != pte->h) {
pvrfmt = FILE_PVR_RECT;
assert(!pteHasMips(pte));
}
}
if (pteHasMips(pte))
pvrfmt += FILE_PVR_MIP_ADD;
if(pte->codebook_size != 256) {
// Write codebook size to GBIX (hack)
WriteFourCC("GBIX", f);
Write32LE(0, f);
Write32LE(pte->codebook_size, f);
}
WriteFourCC("PVRT", f);
Write32LE(chunksize, f); //chunk size
Write32LE(pvrfmt | pte->pixel_format, f); //pixel format, type
Write16LE(pte->w, f);
Write16LE(pte->h, f);
WritePvrTexEncoder(pte, f, pte->auto_small_vq ? PTEW_FILE_PVR_SMALL_VQ : PTEW_NO_SMALL_VQ, 4);
fclose(f);
assert(chunksize + (pte->codebook_size != 256 ? 12 : 0) == FileSize(outfname));
}

View File

@ -0,0 +1,21 @@
#include <stdio.h>
#include <assert.h>
#include "pvr_texture_encoder.h"
#define FILE_PVR_SQUARE (1<<8)
#define FILE_PVR_SQUARE_MIP (2<<8)
#define FILE_PVR_VQ (3<<8)
#define FILE_PVR_VQ_MIP (4<<8)
#define FILE_PVR_4BPP (5<<8) //Assumed
#define FILE_PVR_4BPP_MIP (6<<8) //Assumed
#define FILE_PVR_8BPP (7<<8)
#define FILE_PVR_8BPP_MIP (8<<8) //Assumed
#define FILE_PVR_RECT (9<<8)
#define FILE_PVR_RECT_MIP (10<<8) //Not supported by hardware, but implied
#define FILE_PVR_SMALL_VQ (16<<8)
#define FILE_PVR_SMALL_VQ_MIP (17<<8)
#define FILE_PVR_MIP_ADD (1<<8)
void fPvrWrite(const PvrTexEncoder *td, const char *outfname);
int fPvrSmallVQCodebookSize(int texsize_pixels, int mip);

View File

@ -0,0 +1,76 @@
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include "pvr_texture_encoder.h"
#include "file_common.h"
#include "file_tex.h"
void fTexWrite(const PvrTexEncoder *pte, const char *outfname) {
assert(pte);
FILE *f = fopen(outfname, "w");
assert(f);
unsigned textype = 0;
textype |= pteHasMips(pte) ? (1<<31) : 0;
textype |= pteIsCompressed(pte) ? (1<<30) : 0;
textype |= pte->pixel_format << 27;
textype |= !pte->raw_is_twiddled << 26;
textype |= pteIsStrided(pte) ? (1<<25) : 0;
textype |= (pte->w / 32) & 0x1f;
//Size does not include size of header
unsigned origsize = CalcTextureSize(pte->w, pte->h, pte->pixel_format, pteHasMips(pte), pteIsCompressed(pte), PVR_CODEBOOK_SIZE_BYTES);
unsigned size = ROUND_UP_POW2(origsize, 32);
unsigned paddingamt = size - origsize;
pteLog(LOG_DEBUG, "File DTEX size: (%u + %u) %u\n", origsize, paddingamt, size);
WriteFourCC("DTEX", f);
Write16LE(RoundUpPow2(pte->w), f);
Write16LE(pte->h, f);
Write32LE(textype, f);
Write32LE(size, f);
WritePvrTexEncoder(pte, f, PTEW_NO_SMALL_VQ, 0);
//Pad to 32 bytes
static const unsigned char padding[32] = {0};
pteLog(LOG_DEBUG, "Padding %u\n", paddingamt);
int ret = fwrite(padding, 1, paddingamt, f);
assert(ret == paddingamt);
fclose(f);
}
void fTexWritePaletteAppendPal(const PvrTexEncoder *pte, const char *outfname) {
char palette_name[1024];
size_t namelen = strlen(outfname);
assert(namelen < (sizeof(palette_name) - 5));
memcpy(palette_name, outfname, namelen);
memcpy(palette_name + namelen, ".pal", 4);
palette_name[namelen + 4] = '\0';
fTexWritePalette(pte, palette_name);
}
void fTexWritePalette(const PvrTexEncoder *pte, const char *outfname) {
assert(pte);
assert(pte->palette);
assert(pte->palette_size > 0);
assert(pte->palette_size <= 256);
pteLog(LOG_COMPLETION, "Writing .PAL to \"%s\"...\n", outfname);
FILE *f = fopen(outfname, "w");
assert(f);
WriteFourCC("DPAL", f);
Write32LE(pte->palette_size, f);
for(unsigned i = 0; i < pte->palette_size; i++) {
Write32LE(pxlConvertABGR8888toARGB8888(pte->palette[i]).argb, f);
}
fclose(f);
}

View File

@ -0,0 +1,8 @@
#include <stdio.h>
#include <assert.h>
#include "pvr_texture_encoder.h"
#include "file_common.h"
void fTexWrite(const PvrTexEncoder *td, const char *outfname);
void fTexWritePalette(const PvrTexEncoder *td, const char *outfname);
void fTexWritePaletteAppendPal(const PvrTexEncoder *td, const char *outfname);

87
dreamcast/pvrtex/lfg.c Normal file
View File

@ -0,0 +1,87 @@
/*
* Lagged Fibonacci PRNG
* Copyright (c) 2008 Michael Niedermayer
*
* 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
*/
#include <inttypes.h>
#include <limits.h>
#include <math.h>
#include "lfg.h"
#include "crc.h"
#include "md5.h"
#include "error.h"
#include "intreadwrite.h"
#include "attributes.h"
av_cold void av_lfg_init(AVLFG *c, unsigned int seed)
{
uint8_t tmp[16] = { 0 };
int i;
for (i = 8; i < 64; i += 4) {
AV_WL32(tmp, seed);
tmp[4] = i;
av_md5_sum(tmp, tmp, 16);
c->state[i ] = AV_RL32(tmp);
c->state[i + 1] = AV_RL32(tmp + 4);
c->state[i + 2] = AV_RL32(tmp + 8);
c->state[i + 3] = AV_RL32(tmp + 12);
}
c->index = 0;
}
void av_bmg_get(AVLFG *lfg, double out[2])
{
double x1, x2, w;
do {
x1 = 2.0 / UINT_MAX * av_lfg_get(lfg) - 1.0;
x2 = 2.0 / UINT_MAX * av_lfg_get(lfg) - 1.0;
w = x1 * x1 + x2 * x2;
} while (w >= 1.0);
w = sqrt((-2.0 * log(w)) / w);
out[0] = x1 * w;
out[1] = x2 * w;
}
int av_lfg_init_from_data(AVLFG *c, const uint8_t *data, unsigned int length) {
unsigned int beg, end, segm;
const AVCRC *avcrc;
uint32_t crc = 1;
/* avoid integer overflow in the loop below. */
if (length > (UINT_MAX / 128U)) return AVERROR(EINVAL);
c->index = 0;
avcrc = av_crc_get_table(AV_CRC_32_IEEE); /* This can't fail. It's a well-defined table in crc.c */
/* across 64 segments of the incoming data,
* do a running crc of each segment and store the crc as the state for that slot.
* this works even if the length of the segment is 0 bytes. */
beg = 0;
for (segm = 0;segm < 64;segm++) {
end = (((segm + 1) * length) / 64);
crc = av_crc(avcrc, crc, data + beg, end - beg);
c->state[segm] = (unsigned int)crc;
beg = end;
}
return 0;
}

View File

@ -0,0 +1,59 @@
/*
* Copyright (C) 2007 Vitor Sessak <vitor1001@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
*/
#ifndef AVCODEC_ELBG_H
#define AVCODEC_ELBG_H
#include <stdint.h>
#include "libavutil/lfg.h"
#define av_restrict restrict
struct ELBGContext;
/**
* Implementation of the Enhanced LBG Algorithm
* Based on the paper "Neural Networks 14:1219-1237" that can be found in
* http://citeseer.ist.psu.edu/patan01enhanced.html .
*
* @param ctx A pointer to a pointer to an already allocated ELBGContext
* or a pointer to NULL. In the latter case, this function
* will allocate an ELBGContext and put a pointer to it in `*ctx`.
* @param points Input points.
* @param dim Dimension of the points.
* @param numpoints Num of points in **points.
* @param codebook Pointer to the output codebook. Must be allocated.
* @param num_cb Number of points in the codebook.
* @param num_steps The maximum number of steps. One step is already a good compromise between time and quality.
* @param closest_cb Return the closest codebook to each point. Must be allocated.
* @param rand_state A random number generator state. Should be already initialized by av_lfg_init().
* @param flags Currently unused; must be set to 0.
* @return < 0 in case of error, 0 otherwise
*/
int avpriv_elbg_do(struct ELBGContext **ctx, int *points, int dim,
int numpoints, int *codebook, int num_cb, int num_steps,
int *closest_cb, AVLFG *rand_state, uintptr_t flags);
/**
* Free an ELBGContext and reset the pointer to it.
*/
void avpriv_elbg_free(struct ELBGContext **ctx);
#endif /* AVCODEC_ELBG_H */

View File

@ -0,0 +1,173 @@
/*
* copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
*
* 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
* Macro definitions for various function/variable attributes
*/
#ifndef AVUTIL_ATTRIBUTES_H
#define AVUTIL_ATTRIBUTES_H
#ifdef __GNUC__
# define AV_GCC_VERSION_AT_LEAST(x,y) (__GNUC__ > (x) || __GNUC__ == (x) && __GNUC_MINOR__ >= (y))
# define AV_GCC_VERSION_AT_MOST(x,y) (__GNUC__ < (x) || __GNUC__ == (x) && __GNUC_MINOR__ <= (y))
#else
# define AV_GCC_VERSION_AT_LEAST(x,y) 0
# define AV_GCC_VERSION_AT_MOST(x,y) 0
#endif
#ifdef __has_builtin
# define AV_HAS_BUILTIN(x) __has_builtin(x)
#else
# define AV_HAS_BUILTIN(x) 0
#endif
#ifndef av_always_inline
#if AV_GCC_VERSION_AT_LEAST(3,1)
# define av_always_inline __attribute__((always_inline)) inline
#elif defined(_MSC_VER)
# define av_always_inline __forceinline
#else
# define av_always_inline inline
#endif
#endif
#ifndef av_extern_inline
#if defined(__ICL) && __ICL >= 1210 || defined(__GNUC_STDC_INLINE__)
# define av_extern_inline extern inline
#else
# define av_extern_inline inline
#endif
#endif
#if AV_GCC_VERSION_AT_LEAST(3,4)
# define av_warn_unused_result __attribute__((warn_unused_result))
#else
# define av_warn_unused_result
#endif
#if AV_GCC_VERSION_AT_LEAST(3,1)
# define av_noinline __attribute__((noinline))
#elif defined(_MSC_VER)
# define av_noinline __declspec(noinline)
#else
# define av_noinline
#endif
#if AV_GCC_VERSION_AT_LEAST(3,1) || defined(__clang__)
# define av_pure __attribute__((pure))
#else
# define av_pure
#endif
#if AV_GCC_VERSION_AT_LEAST(2,6) || defined(__clang__)
# define av_const __attribute__((const))
#else
# define av_const
#endif
#if AV_GCC_VERSION_AT_LEAST(4,3) || defined(__clang__)
# define av_cold __attribute__((cold))
#else
# define av_cold
#endif
#if AV_GCC_VERSION_AT_LEAST(4,1) && !defined(__llvm__)
# define av_flatten __attribute__((flatten))
#else
# define av_flatten
#endif
#if AV_GCC_VERSION_AT_LEAST(3,1)
# define attribute_deprecated __attribute__((deprecated))
#elif defined(_MSC_VER)
# define attribute_deprecated __declspec(deprecated)
#else
# define attribute_deprecated
#endif
/**
* Disable warnings about deprecated features
* This is useful for sections of code kept for backward compatibility and
* scheduled for removal.
*/
#ifndef AV_NOWARN_DEPRECATED
#if AV_GCC_VERSION_AT_LEAST(4,6) || defined(__clang__)
# define AV_NOWARN_DEPRECATED(code) \
_Pragma("GCC diagnostic push") \
_Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") \
code \
_Pragma("GCC diagnostic pop")
#elif defined(_MSC_VER)
# define AV_NOWARN_DEPRECATED(code) \
__pragma(warning(push)) \
__pragma(warning(disable : 4996)) \
code; \
__pragma(warning(pop))
#else
# define AV_NOWARN_DEPRECATED(code) code
#endif
#endif
#if defined(__GNUC__) || defined(__clang__)
# define av_unused __attribute__((unused))
#else
# define av_unused
#endif
/**
* Mark a variable as used and prevent the compiler from optimizing it
* away. This is useful for variables accessed only from inline
* assembler without the compiler being aware.
*/
#if AV_GCC_VERSION_AT_LEAST(3,1) || defined(__clang__)
# define av_used __attribute__((used))
#else
# define av_used
#endif
#if AV_GCC_VERSION_AT_LEAST(3,3) || defined(__clang__)
# define av_alias __attribute__((may_alias))
#else
# define av_alias
#endif
#if (defined(__GNUC__) || defined(__clang__)) && !defined(__INTEL_COMPILER)
# define av_uninit(x) x=x
#else
# define av_uninit(x) x
#endif
#if defined(__GNUC__) || defined(__clang__)
# define av_builtin_constant_p __builtin_constant_p
# define av_printf_format(fmtpos, attrpos) __attribute__((__format__(__printf__, fmtpos, attrpos)))
#else
# define av_builtin_constant_p(x) 0
# define av_printf_format(fmtpos, attrpos)
#endif
#if AV_GCC_VERSION_AT_LEAST(2,5) || defined(__clang__)
# define av_noreturn __attribute__((noreturn))
#else
# define av_noreturn
#endif
#endif /* AVUTIL_ATTRIBUTES_H */

View File

@ -0,0 +1,34 @@
/*
* 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
*/
#ifndef AVUTIL_ATTRIBUTES_INTERNAL_H
#define AVUTIL_ATTRIBUTES_INTERNAL_H
#include "attributes.h"
#if (AV_GCC_VERSION_AT_LEAST(4,0) || defined(__clang__)) && (defined(__ELF__) || defined(__MACH__))
# define attribute_visibility_hidden __attribute__((visibility("hidden")))
# define FF_VISIBILITY_PUSH_HIDDEN _Pragma("GCC visibility push(hidden)")
# define FF_VISIBILITY_POP_HIDDEN _Pragma("GCC visibility pop")
#else
# define attribute_visibility_hidden
# define FF_VISIBILITY_PUSH_HIDDEN
# define FF_VISIBILITY_POP_HIDDEN
#endif
#endif /* AVUTIL_ATTRIBUTES_INTERNAL_H */

View File

@ -0,0 +1,75 @@
/*
* copyright (c) 2010 Michael Niedermayer <michaelni@gmx.at>
*
* 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
* simple assert() macros that are a bit more flexible than ISO C assert().
* @author Michael Niedermayer <michaelni@gmx.at>
*/
#ifndef AVUTIL_AVASSERT_H
#define AVUTIL_AVASSERT_H
#include <stdlib.h>
#include "log.h"
#include "macros.h"
/**
* assert() equivalent, that is always enabled.
*/
#define av_assert0(cond) do { \
if (!(cond)) { \
av_log(NULL, AV_LOG_PANIC, "Assertion %s failed at %s:%d\n", \
AV_STRINGIFY(cond), __FILE__, __LINE__); \
abort(); \
} \
} while (0)
/**
* assert() equivalent, that does not lie in speed critical code.
* These asserts() thus can be enabled without fearing speed loss.
*/
#if defined(ASSERT_LEVEL) && ASSERT_LEVEL > 0
#define av_assert1(cond) av_assert0(cond)
#else
#define av_assert1(cond) ((void)0)
#endif
/**
* assert() equivalent, that does lie in speed critical code.
*/
#if defined(ASSERT_LEVEL) && ASSERT_LEVEL > 1
#define av_assert2(cond) av_assert0(cond)
#define av_assert2_fpu() av_assert0_fpu()
#else
#define av_assert2(cond) ((void)0)
#define av_assert2_fpu() ((void)0)
#endif
/**
* Assert that floating point operations can be executed.
*
* This will av_assert0() that the cpu is not in MMX state on X86
*/
void av_assert0_fpu(void);
#endif /* AVUTIL_AVASSERT_H */

View File

@ -0,0 +1,6 @@
/* Generated by ffmpeg configure */
#ifndef AVUTIL_AVCONFIG_H
#define AVUTIL_AVCONFIG_H
#define AV_HAVE_BIGENDIAN 0
#define AV_HAVE_FAST_UNALIGNED 1
#endif /* AVUTIL_AVCONFIG_H */

View File

@ -0,0 +1,429 @@
/*
* Copyright (c) 2007 Mans Rullgard
*
* 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
*/
#ifndef AVUTIL_AVSTRING_H
#define AVUTIL_AVSTRING_H
#include <stddef.h>
#include <stdint.h>
#include "attributes.h"
#include "version.h"
/**
* @addtogroup lavu_string
* @{
*/
/**
* Return non-zero if pfx is a prefix of str. If it is, *ptr is set to
* the address of the first character in str after the prefix.
*
* @param str input string
* @param pfx prefix to test
* @param ptr updated if the prefix is matched inside str
* @return non-zero if the prefix matches, zero otherwise
*/
int av_strstart(const char *str, const char *pfx, const char **ptr);
/**
* Return non-zero if pfx is a prefix of str independent of case. If
* it is, *ptr is set to the address of the first character in str
* after the prefix.
*
* @param str input string
* @param pfx prefix to test
* @param ptr updated if the prefix is matched inside str
* @return non-zero if the prefix matches, zero otherwise
*/
int av_stristart(const char *str, const char *pfx, const char **ptr);
/**
* Locate the first case-independent occurrence in the string haystack
* of the string needle. A zero-length string needle is considered to
* match at the start of haystack.
*
* This function is a case-insensitive version of the standard strstr().
*
* @param haystack string to search in
* @param needle string to search for
* @return pointer to the located match within haystack
* or a null pointer if no match
*/
char *av_stristr(const char *haystack, const char *needle);
/**
* Locate the first occurrence of the string needle in the string haystack
* where not more than hay_length characters are searched. A zero-length
* string needle is considered to match at the start of haystack.
*
* This function is a length-limited version of the standard strstr().
*
* @param haystack string to search in
* @param needle string to search for
* @param hay_length length of string to search in
* @return pointer to the located match within haystack
* or a null pointer if no match
*/
char *av_strnstr(const char *haystack, const char *needle, size_t hay_length);
/**
* Copy the string src to dst, but no more than size - 1 bytes, and
* null-terminate dst.
*
* This function is the same as BSD strlcpy().
*
* @param dst destination buffer
* @param src source string
* @param size size of destination buffer
* @return the length of src
*
* @warning since the return value is the length of src, src absolutely
* _must_ be a properly 0-terminated string, otherwise this will read beyond
* the end of the buffer and possibly crash.
*/
size_t av_strlcpy(char *dst, const char *src, size_t size);
/**
* Append the string src to the string dst, but to a total length of
* no more than size - 1 bytes, and null-terminate dst.
*
* This function is similar to BSD strlcat(), but differs when
* size <= strlen(dst).
*
* @param dst destination buffer
* @param src source string
* @param size size of destination buffer
* @return the total length of src and dst
*
* @warning since the return value use the length of src and dst, these
* absolutely _must_ be a properly 0-terminated strings, otherwise this
* will read beyond the end of the buffer and possibly crash.
*/
size_t av_strlcat(char *dst, const char *src, size_t size);
/**
* Append output to a string, according to a format. Never write out of
* the destination buffer, and always put a terminating 0 within
* the buffer.
* @param dst destination buffer (string to which the output is
* appended)
* @param size total size of the destination buffer
* @param fmt printf-compatible format string, specifying how the
* following parameters are used
* @return the length of the string that would have been generated
* if enough space had been available
*/
size_t av_strlcatf(char *dst, size_t size, const char *fmt, ...) av_printf_format(3, 4);
/**
* Get the count of continuous non zero chars starting from the beginning.
*
* @param s the string whose length to count
* @param len maximum number of characters to check in the string, that
* is the maximum value which is returned by the function
*/
static inline size_t av_strnlen(const char *s, size_t len)
{
size_t i;
for (i = 0; i < len && s[i]; i++)
;
return i;
}
/**
* Print arguments following specified format into a large enough auto
* allocated buffer. It is similar to GNU asprintf().
* @param fmt printf-compatible format string, specifying how the
* following parameters are used.
* @return the allocated string
* @note You have to free the string yourself with av_free().
*/
char *av_asprintf(const char *fmt, ...) av_printf_format(1, 2);
/**
* Unescape the given string until a non escaped terminating char,
* and return the token corresponding to the unescaped string.
*
* The normal \ and ' escaping is supported. Leading and trailing
* whitespaces are removed, unless they are escaped with '\' or are
* enclosed between ''.
*
* @param buf the buffer to parse, buf will be updated to point to the
* terminating char
* @param term a 0-terminated list of terminating chars
* @return the malloced unescaped string, which must be av_freed by
* the user, NULL in case of allocation failure
*/
char *av_get_token(const char **buf, const char *term);
/**
* Split the string into several tokens which can be accessed by
* successive calls to av_strtok().
*
* A token is defined as a sequence of characters not belonging to the
* set specified in delim.
*
* On the first call to av_strtok(), s should point to the string to
* parse, and the value of saveptr is ignored. In subsequent calls, s
* should be NULL, and saveptr should be unchanged since the previous
* call.
*
* This function is similar to strtok_r() defined in POSIX.1.
*
* @param s the string to parse, may be NULL
* @param delim 0-terminated list of token delimiters, must be non-NULL
* @param saveptr user-provided pointer which points to stored
* information necessary for av_strtok() to continue scanning the same
* string. saveptr is updated to point to the next character after the
* first delimiter found, or to NULL if the string was terminated
* @return the found token, or NULL when no token is found
*/
char *av_strtok(char *s, const char *delim, char **saveptr);
/**
* Locale-independent conversion of ASCII isdigit.
*/
static inline av_const int av_isdigit(int c)
{
return c >= '0' && c <= '9';
}
/**
* Locale-independent conversion of ASCII isgraph.
*/
static inline av_const int av_isgraph(int c)
{
return c > 32 && c < 127;
}
/**
* Locale-independent conversion of ASCII isspace.
*/
static inline av_const int av_isspace(int c)
{
return c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' ||
c == '\v';
}
/**
* Locale-independent conversion of ASCII characters to uppercase.
*/
static inline av_const int av_toupper(int c)
{
if (c >= 'a' && c <= 'z')
c ^= 0x20;
return c;
}
/**
* Locale-independent conversion of ASCII characters to lowercase.
*/
static inline av_const int av_tolower(int c)
{
if (c >= 'A' && c <= 'Z')
c ^= 0x20;
return c;
}
/**
* Locale-independent conversion of ASCII isxdigit.
*/
static inline av_const int av_isxdigit(int c)
{
c = av_tolower(c);
return av_isdigit(c) || (c >= 'a' && c <= 'f');
}
/**
* Locale-independent case-insensitive compare.
* @note This means only ASCII-range characters are case-insensitive
*/
int av_strcasecmp(const char *a, const char *b);
/**
* Locale-independent case-insensitive compare.
* @note This means only ASCII-range characters are case-insensitive
*/
int av_strncasecmp(const char *a, const char *b, size_t n);
/**
* Locale-independent strings replace.
* @note This means only ASCII-range characters are replace
*/
char *av_strireplace(const char *str, const char *from, const char *to);
/**
* Thread safe basename.
* @param path the string to parse, on DOS both \ and / are considered separators.
* @return pointer to the basename substring.
* If path does not contain a slash, the function returns a copy of path.
* If path is a NULL pointer or points to an empty string, a pointer
* to a string "." is returned.
*/
const char *av_basename(const char *path);
/**
* Thread safe dirname.
* @param path the string to parse, on DOS both \ and / are considered separators.
* @return A pointer to a string that's the parent directory of path.
* If path is a NULL pointer or points to an empty string, a pointer
* to a string "." is returned.
* @note the function may modify the contents of the path, so copies should be passed.
*/
const char *av_dirname(char *path);
/**
* Match instances of a name in a comma-separated list of names.
* List entries are checked from the start to the end of the names list,
* the first match ends further processing. If an entry prefixed with '-'
* matches, then 0 is returned. The "ALL" list entry is considered to
* match all names.
*
* @param name Name to look for.
* @param names List of names.
* @return 1 on match, 0 otherwise.
*/
int av_match_name(const char *name, const char *names);
/**
* Append path component to the existing path.
* Path separator '/' is placed between when needed.
* Resulting string have to be freed with av_free().
* @param path base path
* @param component component to be appended
* @return new path or NULL on error.
*/
char *av_append_path_component(const char *path, const char *component);
enum AVEscapeMode {
AV_ESCAPE_MODE_AUTO, ///< Use auto-selected escaping mode.
AV_ESCAPE_MODE_BACKSLASH, ///< Use backslash escaping.
AV_ESCAPE_MODE_QUOTE, ///< Use single-quote escaping.
AV_ESCAPE_MODE_XML, ///< Use XML non-markup character data escaping.
};
/**
* Consider spaces special and escape them even in the middle of the
* string.
*
* This is equivalent to adding the whitespace characters to the special
* characters lists, except it is guaranteed to use the exact same list
* of whitespace characters as the rest of libavutil.
*/
#define AV_ESCAPE_FLAG_WHITESPACE (1 << 0)
/**
* Escape only specified special characters.
* Without this flag, escape also any characters that may be considered
* special by av_get_token(), such as the single quote.
*/
#define AV_ESCAPE_FLAG_STRICT (1 << 1)
/**
* Within AV_ESCAPE_MODE_XML, additionally escape single quotes for single
* quoted attributes.
*/
#define AV_ESCAPE_FLAG_XML_SINGLE_QUOTES (1 << 2)
/**
* Within AV_ESCAPE_MODE_XML, additionally escape double quotes for double
* quoted attributes.
*/
#define AV_ESCAPE_FLAG_XML_DOUBLE_QUOTES (1 << 3)
/**
* Escape string in src, and put the escaped string in an allocated
* string in *dst, which must be freed with av_free().
*
* @param dst pointer where an allocated string is put
* @param src string to escape, must be non-NULL
* @param special_chars string containing the special characters which
* need to be escaped, can be NULL
* @param mode escape mode to employ, see AV_ESCAPE_MODE_* macros.
* Any unknown value for mode will be considered equivalent to
* AV_ESCAPE_MODE_BACKSLASH, but this behaviour can change without
* notice.
* @param flags flags which control how to escape, see AV_ESCAPE_FLAG_ macros
* @return the length of the allocated string, or a negative error code in case of error
* @see av_bprint_escape()
*/
av_warn_unused_result
int av_escape(char **dst, const char *src, const char *special_chars,
enum AVEscapeMode mode, int flags);
#define AV_UTF8_FLAG_ACCEPT_INVALID_BIG_CODES 1 ///< accept codepoints over 0x10FFFF
#define AV_UTF8_FLAG_ACCEPT_NON_CHARACTERS 2 ///< accept non-characters - 0xFFFE and 0xFFFF
#define AV_UTF8_FLAG_ACCEPT_SURROGATES 4 ///< accept UTF-16 surrogates codes
#define AV_UTF8_FLAG_EXCLUDE_XML_INVALID_CONTROL_CODES 8 ///< exclude control codes not accepted by XML
#define AV_UTF8_FLAG_ACCEPT_ALL \
AV_UTF8_FLAG_ACCEPT_INVALID_BIG_CODES|AV_UTF8_FLAG_ACCEPT_NON_CHARACTERS|AV_UTF8_FLAG_ACCEPT_SURROGATES
/**
* Read and decode a single UTF-8 code point (character) from the
* buffer in *buf, and update *buf to point to the next byte to
* decode.
*
* In case of an invalid byte sequence, the pointer will be updated to
* the next byte after the invalid sequence and the function will
* return an error code.
*
* Depending on the specified flags, the function will also fail in
* case the decoded code point does not belong to a valid range.
*
* @note For speed-relevant code a carefully implemented use of
* GET_UTF8() may be preferred.
*
* @param codep pointer used to return the parsed code in case of success.
* The value in *codep is set even in case the range check fails.
* @param bufp pointer to the address the first byte of the sequence
* to decode, updated by the function to point to the
* byte next after the decoded sequence
* @param buf_end pointer to the end of the buffer, points to the next
* byte past the last in the buffer. This is used to
* avoid buffer overreads (in case of an unfinished
* UTF-8 sequence towards the end of the buffer).
* @param flags a collection of AV_UTF8_FLAG_* flags
* @return >= 0 in case a sequence was successfully read, a negative
* value in case of invalid sequence
*/
av_warn_unused_result
int av_utf8_decode(int32_t *codep, const uint8_t **bufp, const uint8_t *buf_end,
unsigned int flags);
/**
* Check if a name is in a list.
* @returns 0 if not found, or the 1 based index where it has been found in the
* list.
*/
int av_match_list(const char *name, const char *list, char separator);
/**
* See libc sscanf manual for more information.
* Locale-independent sscanf implementation.
*/
int av_sscanf(const char *string, const char *format, ...);
/**
* @}
*/
#endif /* AVUTIL_AVSTRING_H */

View File

@ -0,0 +1,371 @@
/*
* copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
*
* 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
*/
#ifndef AVUTIL_AVUTIL_H
#define AVUTIL_AVUTIL_H
/**
* @file
* @ingroup lavu
* Convenience header that includes @ref lavu "libavutil"'s core.
*/
/**
* @mainpage
*
* @section ffmpeg_intro Introduction
*
* This document describes the usage of the different libraries
* provided by FFmpeg.
*
* @li @ref libavc "libavcodec" encoding/decoding library
* @li @ref lavfi "libavfilter" graph-based frame editing library
* @li @ref libavf "libavformat" I/O and muxing/demuxing library
* @li @ref lavd "libavdevice" special devices muxing/demuxing library
* @li @ref lavu "libavutil" common utility library
* @li @ref lswr "libswresample" audio resampling, format conversion and mixing
* @li @ref lpp "libpostproc" post processing library
* @li @ref libsws "libswscale" color conversion and scaling library
*
* @section ffmpeg_versioning Versioning and compatibility
*
* Each of the FFmpeg libraries contains a version.h header, which defines a
* major, minor and micro version number with the
* <em>LIBRARYNAME_VERSION_{MAJOR,MINOR,MICRO}</em> macros. The major version
* number is incremented with backward incompatible changes - e.g. removing
* parts of the public API, reordering public struct members, etc. The minor
* version number is incremented for backward compatible API changes or major
* new features - e.g. adding a new public function or a new decoder. The micro
* version number is incremented for smaller changes that a calling program
* might still want to check for - e.g. changing behavior in a previously
* unspecified situation.
*
* FFmpeg guarantees backward API and ABI compatibility for each library as long
* as its major version number is unchanged. This means that no public symbols
* will be removed or renamed. Types and names of the public struct members and
* values of public macros and enums will remain the same (unless they were
* explicitly declared as not part of the public API). Documented behavior will
* not change.
*
* In other words, any correct program that works with a given FFmpeg snapshot
* should work just as well without any changes with any later snapshot with the
* same major versions. This applies to both rebuilding the program against new
* FFmpeg versions or to replacing the dynamic FFmpeg libraries that a program
* links against.
*
* However, new public symbols may be added and new members may be appended to
* public structs whose size is not part of public ABI (most public structs in
* FFmpeg). New macros and enum values may be added. Behavior in undocumented
* situations may change slightly (and be documented). All those are accompanied
* by an entry in doc/APIchanges and incrementing either the minor or micro
* version number.
*/
/**
* @defgroup lavu libavutil
* Common code shared across all FFmpeg libraries.
*
* @note
* libavutil is designed to be modular. In most cases, in order to use the
* functions provided by one component of libavutil you must explicitly include
* the specific header containing that feature. If you are only using
* media-related components, you could simply include libavutil/avutil.h, which
* brings in most of the "core" components.
*
* @{
*
* @defgroup lavu_crypto Crypto and Hashing
*
* @{
* @}
*
* @defgroup lavu_math Mathematics
* @{
*
* @}
*
* @defgroup lavu_string String Manipulation
*
* @{
*
* @}
*
* @defgroup lavu_mem Memory Management
*
* @{
*
* @}
*
* @defgroup lavu_data Data Structures
* @{
*
* @}
*
* @defgroup lavu_video Video related
*
* @{
*
* @}
*
* @defgroup lavu_audio Audio related
*
* @{
*
* @}
*
* @defgroup lavu_error Error Codes
*
* @{
*
* @}
*
* @defgroup lavu_log Logging Facility
*
* @{
*
* @}
*
* @defgroup lavu_misc Other
*
* @{
*
* @defgroup preproc_misc Preprocessor String Macros
*
* @{
*
* @}
*
* @defgroup version_utils Library Version Macros
*
* @{
*
* @}
*/
/**
* @addtogroup lavu_ver
* @{
*/
/**
* Return the LIBAVUTIL_VERSION_INT constant.
*/
unsigned avutil_version(void);
/**
* Return an informative version string. This usually is the actual release
* version number or a git commit description. This string has no fixed format
* and can change any time. It should never be parsed by code.
*/
const char *av_version_info(void);
/**
* Return the libavutil build-time configuration.
*/
const char *avutil_configuration(void);
/**
* Return the libavutil license.
*/
const char *avutil_license(void);
/**
* @}
*/
/**
* @addtogroup lavu_media Media Type
* @brief Media Type
*/
enum AVMediaType {
AVMEDIA_TYPE_UNKNOWN = -1, ///< Usually treated as AVMEDIA_TYPE_DATA
AVMEDIA_TYPE_VIDEO,
AVMEDIA_TYPE_AUDIO,
AVMEDIA_TYPE_DATA, ///< Opaque data information usually continuous
AVMEDIA_TYPE_SUBTITLE,
AVMEDIA_TYPE_ATTACHMENT, ///< Opaque data information usually sparse
AVMEDIA_TYPE_NB
};
/**
* Return a string describing the media_type enum, NULL if media_type
* is unknown.
*/
const char *av_get_media_type_string(enum AVMediaType media_type);
/**
* @defgroup lavu_const Constants
* @{
*
* @defgroup lavu_enc Encoding specific
*
* @note those definition should move to avcodec
* @{
*/
#define FF_LAMBDA_SHIFT 7
#define FF_LAMBDA_SCALE (1<<FF_LAMBDA_SHIFT)
#define FF_QP2LAMBDA 118 ///< factor to convert from H.263 QP to lambda
#define FF_LAMBDA_MAX (256*128-1)
#define FF_QUALITY_SCALE FF_LAMBDA_SCALE //FIXME maybe remove
/**
* @}
* @defgroup lavu_time Timestamp specific
*
* FFmpeg internal timebase and timestamp definitions
*
* @{
*/
/**
* @brief Undefined timestamp value
*
* Usually reported by demuxer that work on containers that do not provide
* either pts or dts.
*/
#define AV_NOPTS_VALUE ((int64_t)UINT64_C(0x8000000000000000))
/**
* Internal time base represented as integer
*/
#define AV_TIME_BASE 1000000
/**
* Internal time base represented as fractional value
*/
#define AV_TIME_BASE_Q (AVRational){1, AV_TIME_BASE}
/**
* @}
* @}
* @defgroup lavu_picture Image related
*
* AVPicture types, pixel formats and basic image planes manipulation.
*
* @{
*/
enum AVPictureType {
AV_PICTURE_TYPE_NONE = 0, ///< Undefined
AV_PICTURE_TYPE_I, ///< Intra
AV_PICTURE_TYPE_P, ///< Predicted
AV_PICTURE_TYPE_B, ///< Bi-dir predicted
AV_PICTURE_TYPE_S, ///< S(GMC)-VOP MPEG-4
AV_PICTURE_TYPE_SI, ///< Switching Intra
AV_PICTURE_TYPE_SP, ///< Switching Predicted
AV_PICTURE_TYPE_BI, ///< BI type
};
/**
* Return a single letter to describe the given picture type
* pict_type.
*
* @param[in] pict_type the picture type @return a single character
* representing the picture type, '?' if pict_type is unknown
*/
char av_get_picture_type_char(enum AVPictureType pict_type);
/**
* @}
*/
#include "common.h"
#include "error.h"
#include "rational.h"
#include "version.h"
#include "macros.h"
#include "mathematics.h"
#include "log.h"
#include "pixfmt.h"
/**
* Return x default pointer in case p is NULL.
*/
static inline void *av_x_if_null(const void *p, const void *x)
{
return (void *)(intptr_t)(p ? p : x);
}
/**
* Compute the length of an integer list.
*
* @param elsize size in bytes of each list element (only 1, 2, 4 or 8)
* @param term list terminator (usually 0 or -1)
* @param list pointer to the list
* @return length of the list, in elements, not counting the terminator
*/
unsigned av_int_list_length_for_size(unsigned elsize,
const void *list, uint64_t term) av_pure;
/**
* Compute the length of an integer list.
*
* @param term list terminator (usually 0 or -1)
* @param list pointer to the list
* @return length of the list, in elements, not counting the terminator
*/
#define av_int_list_length(list, term) \
av_int_list_length_for_size(sizeof(*(list)), list, term)
#if FF_API_AV_FOPEN_UTF8
/**
* Open a file using a UTF-8 filename.
* The API of this function matches POSIX fopen(), errors are returned through
* errno.
* @deprecated Avoid using it, as on Windows, the FILE* allocated by this
* function may be allocated with a different CRT than the caller
* who uses the FILE*. No replacement provided in public API.
*/
attribute_deprecated
FILE *av_fopen_utf8(const char *path, const char *mode);
#endif
/**
* Return the fractional representation of the internal time base.
*/
AVRational av_get_time_base_q(void);
#define AV_FOURCC_MAX_STRING_SIZE 32
#define av_fourcc2str(fourcc) av_fourcc_make_string((char[AV_FOURCC_MAX_STRING_SIZE]){0}, fourcc)
/**
* Fill the provided buffer with a string containing a FourCC (four-character
* code) representation.
*
* @param buf a buffer with size in bytes of at least AV_FOURCC_MAX_STRING_SIZE
* @param fourcc the fourcc to represent
* @return the buffer in input
*/
char *av_fourcc_make_string(char *buf, uint32_t fourcc);
/**
* @}
* @}
*/
#endif /* AVUTIL_AVUTIL_H */

View File

@ -0,0 +1,251 @@
/*
* Copyright (c) 2012 Nicolas George
*
* 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
* @ingroup lavu_avbprint
* AVBPrint public header
*/
#ifndef AVUTIL_BPRINT_H
#define AVUTIL_BPRINT_H
#include <stdarg.h>
#include "attributes.h"
#include "avstring.h"
/**
* @defgroup lavu_avbprint AVBPrint
* @ingroup lavu_data
*
* A buffer to print data progressively
* @{
*/
/**
* Define a structure with extra padding to a fixed size
* This helps ensuring binary compatibility with future versions.
*/
#define FF_PAD_STRUCTURE(name, size, ...) \
struct ff_pad_helper_##name { __VA_ARGS__ }; \
typedef struct name { \
__VA_ARGS__ \
char reserved_padding[size - sizeof(struct ff_pad_helper_##name)]; \
} name;
/**
* Buffer to print data progressively
*
* The string buffer grows as necessary and is always 0-terminated.
* The content of the string is never accessed, and thus is
* encoding-agnostic and can even hold binary data.
*
* Small buffers are kept in the structure itself, and thus require no
* memory allocation at all (unless the contents of the buffer is needed
* after the structure goes out of scope). This is almost as lightweight as
* declaring a local `char buf[512]`.
*
* The length of the string can go beyond the allocated size: the buffer is
* then truncated, but the functions still keep account of the actual total
* length.
*
* In other words, AVBPrint.len can be greater than AVBPrint.size and records
* the total length of what would have been to the buffer if there had been
* enough memory.
*
* Append operations do not need to be tested for failure: if a memory
* allocation fails, data stop being appended to the buffer, but the length
* is still updated. This situation can be tested with
* av_bprint_is_complete().
*
* The AVBPrint.size_max field determines several possible behaviours:
* - `size_max = -1` (= `UINT_MAX`) or any large value will let the buffer be
* reallocated as necessary, with an amortized linear cost.
* - `size_max = 0` prevents writing anything to the buffer: only the total
* length is computed. The write operations can then possibly be repeated in
* a buffer with exactly the necessary size
* (using `size_init = size_max = len + 1`).
* - `size_max = 1` is automatically replaced by the exact size available in the
* structure itself, thus ensuring no dynamic memory allocation. The
* internal buffer is large enough to hold a reasonable paragraph of text,
* such as the current paragraph.
*/
FF_PAD_STRUCTURE(AVBPrint, 1024,
char *str; /**< string so far */
unsigned len; /**< length so far */
unsigned size; /**< allocated memory */
unsigned size_max; /**< maximum allocated memory */
char reserved_internal_buffer[1];
)
/**
* @name Max size special values
* Convenience macros for special values for av_bprint_init() size_max
* parameter.
* @{
*/
/**
* Buffer will be reallocated as necessary, with an amortized linear cost.
*/
#define AV_BPRINT_SIZE_UNLIMITED ((unsigned)-1)
/**
* Use the exact size available in the AVBPrint structure itself.
*
* Thus ensuring no dynamic memory allocation. The internal buffer is large
* enough to hold a reasonable paragraph of text, such as the current paragraph.
*/
#define AV_BPRINT_SIZE_AUTOMATIC 1
/**
* Do not write anything to the buffer, only calculate the total length.
*
* The write operations can then possibly be repeated in a buffer with
* exactly the necessary size (using `size_init = size_max = AVBPrint.len + 1`).
*/
#define AV_BPRINT_SIZE_COUNT_ONLY 0
/** @} */
/**
* Init a print buffer.
*
* @param buf buffer to init
* @param size_init initial size (including the final 0)
* @param size_max maximum size;
* - `0` means do not write anything, just count the length
* - `1` is replaced by the maximum value for automatic storage
* any large value means that the internal buffer will be
* reallocated as needed up to that limit
* - `-1` is converted to `UINT_MAX`, the largest limit possible.
* Check also `AV_BPRINT_SIZE_*` macros.
*/
void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max);
/**
* Init a print buffer using a pre-existing buffer.
*
* The buffer will not be reallocated.
*
* @param buf buffer structure to init
* @param buffer byte buffer to use for the string data
* @param size size of buffer
*/
void av_bprint_init_for_buffer(AVBPrint *buf, char *buffer, unsigned size);
/**
* Append a formatted string to a print buffer.
*/
void av_bprintf(AVBPrint *buf, const char *fmt, ...) av_printf_format(2, 3);
/**
* Append a formatted string to a print buffer.
*/
void av_vbprintf(AVBPrint *buf, const char *fmt, va_list vl_arg);
/**
* Append char c n times to a print buffer.
*/
void av_bprint_chars(AVBPrint *buf, char c, unsigned n);
/**
* Append data to a print buffer.
*
* param buf bprint buffer to use
* param data pointer to data
* param size size of data
*/
void av_bprint_append_data(AVBPrint *buf, const char *data, unsigned size);
struct tm;
/**
* Append a formatted date and time to a print buffer.
*
* param buf bprint buffer to use
* param fmt date and time format string, see strftime()
* param tm broken-down time structure to translate
*
* @note due to poor design of the standard strftime function, it may
* produce poor results if the format string expands to a very long text and
* the bprint buffer is near the limit stated by the size_max option.
*/
void av_bprint_strftime(AVBPrint *buf, const char *fmt, const struct tm *tm);
/**
* Allocate bytes in the buffer for external use.
*
* @param[in] buf buffer structure
* @param[in] size required size
* @param[out] mem pointer to the memory area
* @param[out] actual_size size of the memory area after allocation;
* can be larger or smaller than size
*/
void av_bprint_get_buffer(AVBPrint *buf, unsigned size,
unsigned char **mem, unsigned *actual_size);
/**
* Reset the string to "" but keep internal allocated data.
*/
void av_bprint_clear(AVBPrint *buf);
/**
* Test if the print buffer is complete (not truncated).
*
* It may have been truncated due to a memory allocation failure
* or the size_max limit (compare size and size_max if necessary).
*/
static inline int av_bprint_is_complete(const AVBPrint *buf)
{
return buf->len < buf->size;
}
/**
* Finalize a print buffer.
*
* The print buffer can no longer be used afterwards,
* but the len and size fields are still valid.
*
* @arg[out] ret_str if not NULL, used to return a permanent copy of the
* buffer contents, or NULL if memory allocation fails;
* if NULL, the buffer is discarded and freed
* @return 0 for success or error code (probably AVERROR(ENOMEM))
*/
int av_bprint_finalize(AVBPrint *buf, char **ret_str);
/**
* Escape the content in src and append it to dstbuf.
*
* @param dstbuf already inited destination bprint buffer
* @param src string containing the text to escape
* @param special_chars string containing the special characters which
* need to be escaped, can be NULL
* @param mode escape mode to employ, see AV_ESCAPE_MODE_* macros.
* Any unknown value for mode will be considered equivalent to
* AV_ESCAPE_MODE_BACKSLASH, but this behaviour can change without
* notice.
* @param flags flags which control how to escape, see AV_ESCAPE_FLAG_* macros
*/
void av_bprint_escape(AVBPrint *dstbuf, const char *src, const char *special_chars,
enum AVEscapeMode mode, int flags);
/** @} */
#endif /* AVUTIL_BPRINT_H */

View File

@ -0,0 +1,111 @@
/*
* copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
*
* 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
* byte swapping routines
*/
#ifndef AVUTIL_BSWAP_H
#define AVUTIL_BSWAP_H
#include <stdint.h>
#include "libavutil/avconfig.h"
#include "attributes.h"
#ifdef HAVE_AV_CONFIG_H
#include "config.h"
#if ARCH_AARCH64
# include "aarch64/bswap.h"
#elif ARCH_ARM
# include "arm/bswap.h"
#elif ARCH_AVR32
# include "avr32/bswap.h"
#elif ARCH_RISCV
# include "riscv/bswap.h"
#elif ARCH_SH4
# include "sh4/bswap.h"
#elif ARCH_X86
# include "x86/bswap.h"
#endif
#endif /* HAVE_AV_CONFIG_H */
#define AV_BSWAP16C(x) (((x) << 8 & 0xff00) | ((x) >> 8 & 0x00ff))
#define AV_BSWAP32C(x) (AV_BSWAP16C(x) << 16 | AV_BSWAP16C((x) >> 16))
#define AV_BSWAP64C(x) (AV_BSWAP32C(x) << 32 | AV_BSWAP32C((x) >> 32))
#define AV_BSWAPC(s, x) AV_BSWAP##s##C(x)
#ifndef av_bswap16
static av_always_inline av_const uint16_t av_bswap16(uint16_t x)
{
x= (x>>8) | (x<<8);
return x;
}
#endif
#ifndef av_bswap32
static av_always_inline av_const uint32_t av_bswap32(uint32_t x)
{
return AV_BSWAP32C(x);
}
#endif
#ifndef av_bswap64
static inline uint64_t av_const av_bswap64(uint64_t x)
{
return (uint64_t)av_bswap32(x) << 32 | av_bswap32(x >> 32);
}
#endif
// be2ne ... big-endian to native-endian
// le2ne ... little-endian to native-endian
#if AV_HAVE_BIGENDIAN
#define av_be2ne16(x) (x)
#define av_be2ne32(x) (x)
#define av_be2ne64(x) (x)
#define av_le2ne16(x) av_bswap16(x)
#define av_le2ne32(x) av_bswap32(x)
#define av_le2ne64(x) av_bswap64(x)
#define AV_BE2NEC(s, x) (x)
#define AV_LE2NEC(s, x) AV_BSWAPC(s, x)
#else
#define av_be2ne16(x) av_bswap16(x)
#define av_be2ne32(x) av_bswap32(x)
#define av_be2ne64(x) av_bswap64(x)
#define av_le2ne16(x) (x)
#define av_le2ne32(x) (x)
#define av_le2ne64(x) (x)
#define AV_BE2NEC(s, x) AV_BSWAPC(s, x)
#define AV_LE2NEC(s, x) (x)
#endif
#define AV_BE2NE16C(x) AV_BE2NEC(16, x)
#define AV_BE2NE32C(x) AV_BE2NEC(32, x)
#define AV_BE2NE64C(x) AV_BE2NEC(64, x)
#define AV_LE2NE16C(x) AV_LE2NEC(16, x)
#define AV_LE2NE32C(x) AV_LE2NEC(32, x)
#define AV_LE2NE64C(x) AV_LE2NEC(64, x)
#endif /* AVUTIL_BSWAP_H */

View File

@ -0,0 +1,578 @@
/*
* copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
*
* 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
* common internal and external API header
*/
#ifndef AVUTIL_COMMON_H
#define AVUTIL_COMMON_H
#if defined(__cplusplus) && !defined(__STDC_CONSTANT_MACROS) && !defined(UINT64_C)
#error missing -D__STDC_CONSTANT_MACROS / #define __STDC_CONSTANT_MACROS
#endif
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "attributes.h"
#include "macros.h"
//rounded division & shift
#define RSHIFT(a,b) ((a) > 0 ? ((a) + ((1<<(b))>>1))>>(b) : ((a) + ((1<<(b))>>1)-1)>>(b))
/* assume b>0 */
#define ROUNDED_DIV(a,b) (((a)>=0 ? (a) + ((b)>>1) : (a) - ((b)>>1))/(b))
/* Fast a/(1<<b) rounded toward +inf. Assume a>=0 and b>=0 */
#define AV_CEIL_RSHIFT(a,b) (!av_builtin_constant_p(b) ? -((-(a)) >> (b)) \
: ((a) + (1<<(b)) - 1) >> (b))
/* Backwards compat. */
#define FF_CEIL_RSHIFT AV_CEIL_RSHIFT
#define FFUDIV(a,b) (((a)>0 ?(a):(a)-(b)+1) / (b))
#define FFUMOD(a,b) ((a)-(b)*FFUDIV(a,b))
/**
* Absolute value, Note, INT_MIN / INT64_MIN result in undefined behavior as they
* are not representable as absolute values of their type. This is the same
* as with *abs()
* @see FFNABS()
*/
#define FFABS(a) ((a) >= 0 ? (a) : (-(a)))
#define FFSIGN(a) ((a) > 0 ? 1 : -1)
/**
* Negative Absolute value.
* this works for all integers of all types.
* As with many macros, this evaluates its argument twice, it thus must not have
* a sideeffect, that is FFNABS(x++) has undefined behavior.
*/
#define FFNABS(a) ((a) <= 0 ? (a) : (-(a)))
/**
* Unsigned Absolute value.
* This takes the absolute value of a signed int and returns it as a unsigned.
* This also works with INT_MIN which would otherwise not be representable
* As with many macros, this evaluates its argument twice.
*/
#define FFABSU(a) ((a) <= 0 ? -(unsigned)(a) : (unsigned)(a))
#define FFABS64U(a) ((a) <= 0 ? -(uint64_t)(a) : (uint64_t)(a))
/* misc math functions */
#ifdef HAVE_AV_CONFIG_H
# include "config.h"
# include "intmath.h"
#endif
#ifndef av_ceil_log2
# define av_ceil_log2 av_ceil_log2_c
#endif
#ifndef av_clip
# define av_clip av_clip_c
#endif
#ifndef av_clip64
# define av_clip64 av_clip64_c
#endif
#ifndef av_clip_uint8
# define av_clip_uint8 av_clip_uint8_c
#endif
#ifndef av_clip_int8
# define av_clip_int8 av_clip_int8_c
#endif
#ifndef av_clip_uint16
# define av_clip_uint16 av_clip_uint16_c
#endif
#ifndef av_clip_int16
# define av_clip_int16 av_clip_int16_c
#endif
#ifndef av_clipl_int32
# define av_clipl_int32 av_clipl_int32_c
#endif
#ifndef av_clip_intp2
# define av_clip_intp2 av_clip_intp2_c
#endif
#ifndef av_clip_uintp2
# define av_clip_uintp2 av_clip_uintp2_c
#endif
#ifndef av_mod_uintp2
# define av_mod_uintp2 av_mod_uintp2_c
#endif
#ifndef av_sat_add32
# define av_sat_add32 av_sat_add32_c
#endif
#ifndef av_sat_dadd32
# define av_sat_dadd32 av_sat_dadd32_c
#endif
#ifndef av_sat_sub32
# define av_sat_sub32 av_sat_sub32_c
#endif
#ifndef av_sat_dsub32
# define av_sat_dsub32 av_sat_dsub32_c
#endif
#ifndef av_sat_add64
# define av_sat_add64 av_sat_add64_c
#endif
#ifndef av_sat_sub64
# define av_sat_sub64 av_sat_sub64_c
#endif
#ifndef av_clipf
# define av_clipf av_clipf_c
#endif
#ifndef av_clipd
# define av_clipd av_clipd_c
#endif
#ifndef av_popcount
# define av_popcount av_popcount_c
#endif
#ifndef av_popcount64
# define av_popcount64 av_popcount64_c
#endif
#ifndef av_parity
# define av_parity av_parity_c
#endif
#ifndef av_log2
av_const int av_log2(unsigned v);
#endif
#ifndef av_log2_16bit
av_const int av_log2_16bit(unsigned v);
#endif
/**
* Clip a signed integer value into the amin-amax range.
* @param a value to clip
* @param amin minimum value of the clip range
* @param amax maximum value of the clip range
* @return clipped value
*/
static av_always_inline av_const int av_clip_c(int a, int amin, int amax)
{
#if defined(HAVE_AV_CONFIG_H) && defined(ASSERT_LEVEL) && ASSERT_LEVEL >= 2
if (amin > amax) abort();
#endif
if (a < amin) return amin;
else if (a > amax) return amax;
else return a;
}
/**
* Clip a signed 64bit integer value into the amin-amax range.
* @param a value to clip
* @param amin minimum value of the clip range
* @param amax maximum value of the clip range
* @return clipped value
*/
static av_always_inline av_const int64_t av_clip64_c(int64_t a, int64_t amin, int64_t amax)
{
#if defined(HAVE_AV_CONFIG_H) && defined(ASSERT_LEVEL) && ASSERT_LEVEL >= 2
if (amin > amax) abort();
#endif
if (a < amin) return amin;
else if (a > amax) return amax;
else return a;
}
/**
* Clip a signed integer value into the 0-255 range.
* @param a value to clip
* @return clipped value
*/
static av_always_inline av_const uint8_t av_clip_uint8_c(int a)
{
if (a&(~0xFF)) return (~a)>>31;
else return a;
}
/**
* Clip a signed integer value into the -128,127 range.
* @param a value to clip
* @return clipped value
*/
static av_always_inline av_const int8_t av_clip_int8_c(int a)
{
if ((a+0x80U) & ~0xFF) return (a>>31) ^ 0x7F;
else return a;
}
/**
* Clip a signed integer value into the 0-65535 range.
* @param a value to clip
* @return clipped value
*/
static av_always_inline av_const uint16_t av_clip_uint16_c(int a)
{
if (a&(~0xFFFF)) return (~a)>>31;
else return a;
}
/**
* Clip a signed integer value into the -32768,32767 range.
* @param a value to clip
* @return clipped value
*/
static av_always_inline av_const int16_t av_clip_int16_c(int a)
{
if ((a+0x8000U) & ~0xFFFF) return (a>>31) ^ 0x7FFF;
else return a;
}
/**
* Clip a signed 64-bit integer value into the -2147483648,2147483647 range.
* @param a value to clip
* @return clipped value
*/
static av_always_inline av_const int32_t av_clipl_int32_c(int64_t a)
{
if ((a+0x80000000u) & ~UINT64_C(0xFFFFFFFF)) return (int32_t)((a>>63) ^ 0x7FFFFFFF);
else return (int32_t)a;
}
/**
* Clip a signed integer into the -(2^p),(2^p-1) range.
* @param a value to clip
* @param p bit position to clip at
* @return clipped value
*/
static av_always_inline av_const int av_clip_intp2_c(int a, int p)
{
if (((unsigned)a + (1 << p)) & ~((2 << p) - 1))
return (a >> 31) ^ ((1 << p) - 1);
else
return a;
}
/**
* Clip a signed integer to an unsigned power of two range.
* @param a value to clip
* @param p bit position to clip at
* @return clipped value
*/
static av_always_inline av_const unsigned av_clip_uintp2_c(int a, int p)
{
if (a & ~((1<<p) - 1)) return (~a) >> 31 & ((1<<p) - 1);
else return a;
}
/**
* Clear high bits from an unsigned integer starting with specific bit position
* @param a value to clip
* @param p bit position to clip at
* @return clipped value
*/
static av_always_inline av_const unsigned av_mod_uintp2_c(unsigned a, unsigned p)
{
return a & ((1U << p) - 1);
}
/**
* Add two signed 32-bit values with saturation.
*
* @param a one value
* @param b another value
* @return sum with signed saturation
*/
static av_always_inline int av_sat_add32_c(int a, int b)
{
return av_clipl_int32((int64_t)a + b);
}
/**
* Add a doubled value to another value with saturation at both stages.
*
* @param a first value
* @param b value doubled and added to a
* @return sum sat(a + sat(2*b)) with signed saturation
*/
static av_always_inline int av_sat_dadd32_c(int a, int b)
{
return av_sat_add32(a, av_sat_add32(b, b));
}
/**
* Subtract two signed 32-bit values with saturation.
*
* @param a one value
* @param b another value
* @return difference with signed saturation
*/
static av_always_inline int av_sat_sub32_c(int a, int b)
{
return av_clipl_int32((int64_t)a - b);
}
/**
* Subtract a doubled value from another value with saturation at both stages.
*
* @param a first value
* @param b value doubled and subtracted from a
* @return difference sat(a - sat(2*b)) with signed saturation
*/
static av_always_inline int av_sat_dsub32_c(int a, int b)
{
return av_sat_sub32(a, av_sat_add32(b, b));
}
/**
* Add two signed 64-bit values with saturation.
*
* @param a one value
* @param b another value
* @return sum with signed saturation
*/
static av_always_inline int64_t av_sat_add64_c(int64_t a, int64_t b) {
#if (!defined(__INTEL_COMPILER) && AV_GCC_VERSION_AT_LEAST(5,1)) || AV_HAS_BUILTIN(__builtin_add_overflow)
int64_t tmp;
return !__builtin_add_overflow(a, b, &tmp) ? tmp : (tmp < 0 ? INT64_MAX : INT64_MIN);
#else
int64_t s = a+(uint64_t)b;
if ((int64_t)(a^b | ~s^b) >= 0)
return INT64_MAX ^ (b >> 63);
return s;
#endif
}
/**
* Subtract two signed 64-bit values with saturation.
*
* @param a one value
* @param b another value
* @return difference with signed saturation
*/
static av_always_inline int64_t av_sat_sub64_c(int64_t a, int64_t b) {
#if (!defined(__INTEL_COMPILER) && AV_GCC_VERSION_AT_LEAST(5,1)) || AV_HAS_BUILTIN(__builtin_sub_overflow)
int64_t tmp;
return !__builtin_sub_overflow(a, b, &tmp) ? tmp : (tmp < 0 ? INT64_MAX : INT64_MIN);
#else
if (b <= 0 && a >= INT64_MAX + b)
return INT64_MAX;
if (b >= 0 && a <= INT64_MIN + b)
return INT64_MIN;
return a - b;
#endif
}
/**
* Clip a float value into the amin-amax range.
* If a is nan or -inf amin will be returned.
* If a is +inf amax will be returned.
* @param a value to clip
* @param amin minimum value of the clip range
* @param amax maximum value of the clip range
* @return clipped value
*/
static av_always_inline av_const float av_clipf_c(float a, float amin, float amax)
{
#if defined(HAVE_AV_CONFIG_H) && defined(ASSERT_LEVEL) && ASSERT_LEVEL >= 2
if (amin > amax) abort();
#endif
return FFMIN(FFMAX(a, amin), amax);
}
/**
* Clip a double value into the amin-amax range.
* If a is nan or -inf amin will be returned.
* If a is +inf amax will be returned.
* @param a value to clip
* @param amin minimum value of the clip range
* @param amax maximum value of the clip range
* @return clipped value
*/
static av_always_inline av_const double av_clipd_c(double a, double amin, double amax)
{
#if defined(HAVE_AV_CONFIG_H) && defined(ASSERT_LEVEL) && ASSERT_LEVEL >= 2
if (amin > amax) abort();
#endif
return FFMIN(FFMAX(a, amin), amax);
}
/** Compute ceil(log2(x)).
* @param x value used to compute ceil(log2(x))
* @return computed ceiling of log2(x)
*/
static av_always_inline av_const int av_ceil_log2_c(int x)
{
return av_log2((x - 1U) << 1);
}
/**
* Count number of bits set to one in x
* @param x value to count bits of
* @return the number of bits set to one in x
*/
static av_always_inline av_const int av_popcount_c(uint32_t x)
{
x -= (x >> 1) & 0x55555555;
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
x = (x + (x >> 4)) & 0x0F0F0F0F;
x += x >> 8;
return (x + (x >> 16)) & 0x3F;
}
/**
* Count number of bits set to one in x
* @param x value to count bits of
* @return the number of bits set to one in x
*/
static av_always_inline av_const int av_popcount64_c(uint64_t x)
{
return av_popcount((uint32_t)x) + av_popcount((uint32_t)(x >> 32));
}
static av_always_inline av_const int av_parity_c(uint32_t v)
{
return av_popcount(v) & 1;
}
/**
* Convert a UTF-8 character (up to 4 bytes) to its 32-bit UCS-4 encoded form.
*
* @param val Output value, must be an lvalue of type uint32_t.
* @param GET_BYTE Expression reading one byte from the input.
* Evaluated up to 7 times (4 for the currently
* assigned Unicode range). With a memory buffer
* input, this could be *ptr++, or if you want to make sure
* that *ptr stops at the end of a NULL terminated string then
* *ptr ? *ptr++ : 0
* @param ERROR Expression to be evaluated on invalid input,
* typically a goto statement.
*
* @warning ERROR should not contain a loop control statement which
* could interact with the internal while loop, and should force an
* exit from the macro code (e.g. through a goto or a return) in order
* to prevent undefined results.
*/
#define GET_UTF8(val, GET_BYTE, ERROR)\
val= (GET_BYTE);\
{\
uint32_t top = (val & 128) >> 1;\
if ((val & 0xc0) == 0x80 || val >= 0xFE)\
{ERROR}\
while (val & top) {\
unsigned int tmp = (GET_BYTE) - 128;\
if(tmp>>6)\
{ERROR}\
val= (val<<6) + tmp;\
top <<= 5;\
}\
val &= (top << 1) - 1;\
}
/**
* Convert a UTF-16 character (2 or 4 bytes) to its 32-bit UCS-4 encoded form.
*
* @param val Output value, must be an lvalue of type uint32_t.
* @param GET_16BIT Expression returning two bytes of UTF-16 data converted
* to native byte order. Evaluated one or two times.
* @param ERROR Expression to be evaluated on invalid input,
* typically a goto statement.
*/
#define GET_UTF16(val, GET_16BIT, ERROR)\
val = (GET_16BIT);\
{\
unsigned int hi = val - 0xD800;\
if (hi < 0x800) {\
val = (GET_16BIT) - 0xDC00;\
if (val > 0x3FFU || hi > 0x3FFU)\
{ERROR}\
val += (hi<<10) + 0x10000;\
}\
}\
/**
* @def PUT_UTF8(val, tmp, PUT_BYTE)
* Convert a 32-bit Unicode character to its UTF-8 encoded form (up to 4 bytes long).
* @param val is an input-only argument and should be of type uint32_t. It holds
* a UCS-4 encoded Unicode character that is to be converted to UTF-8. If
* val is given as a function it is executed only once.
* @param tmp is a temporary variable and should be of type uint8_t. It
* represents an intermediate value during conversion that is to be
* output by PUT_BYTE.
* @param PUT_BYTE writes the converted UTF-8 bytes to any proper destination.
* It could be a function or a statement, and uses tmp as the input byte.
* For example, PUT_BYTE could be "*output++ = tmp;" PUT_BYTE will be
* executed up to 4 times for values in the valid UTF-8 range and up to
* 7 times in the general case, depending on the length of the converted
* Unicode character.
*/
#define PUT_UTF8(val, tmp, PUT_BYTE)\
{\
int bytes, shift;\
uint32_t in = val;\
if (in < 0x80) {\
tmp = in;\
PUT_BYTE\
} else {\
bytes = (av_log2(in) + 4) / 5;\
shift = (bytes - 1) * 6;\
tmp = (256 - (256 >> bytes)) | (in >> shift);\
PUT_BYTE\
while (shift >= 6) {\
shift -= 6;\
tmp = 0x80 | ((in >> shift) & 0x3f);\
PUT_BYTE\
}\
}\
}
/**
* @def PUT_UTF16(val, tmp, PUT_16BIT)
* Convert a 32-bit Unicode character to its UTF-16 encoded form (2 or 4 bytes).
* @param val is an input-only argument and should be of type uint32_t. It holds
* a UCS-4 encoded Unicode character that is to be converted to UTF-16. If
* val is given as a function it is executed only once.
* @param tmp is a temporary variable and should be of type uint16_t. It
* represents an intermediate value during conversion that is to be
* output by PUT_16BIT.
* @param PUT_16BIT writes the converted UTF-16 data to any proper destination
* in desired endianness. It could be a function or a statement, and uses tmp
* as the input byte. For example, PUT_BYTE could be "*output++ = tmp;"
* PUT_BYTE will be executed 1 or 2 times depending on input character.
*/
#define PUT_UTF16(val, tmp, PUT_16BIT)\
{\
uint32_t in = val;\
if (in < 0x10000) {\
tmp = in;\
PUT_16BIT\
} else {\
tmp = 0xD800 | ((in - 0x10000) >> 10);\
PUT_16BIT\
tmp = 0xDC00 | ((in - 0x10000) & 0x3FF);\
PUT_16BIT\
}\
}\
#include "mem.h"
#ifdef HAVE_AV_CONFIG_H
# include "internal.h"
#endif /* HAVE_AV_CONFIG_H */
#endif /* AVUTIL_COMMON_H */

View File

@ -0,0 +1,102 @@
/*
* copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
*
* 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
* @ingroup lavu_crc32
* Public header for CRC hash function implementation.
*/
#ifndef AVUTIL_CRC_H
#define AVUTIL_CRC_H
#include <stdint.h>
#include <stddef.h>
#include "attributes.h"
/**
* @defgroup lavu_crc32 CRC
* @ingroup lavu_hash
* CRC (Cyclic Redundancy Check) hash function implementation.
*
* This module supports numerous CRC polynomials, in addition to the most
* widely used CRC-32-IEEE. See @ref AVCRCId for a list of available
* polynomials.
*
* @{
*/
typedef uint32_t AVCRC;
typedef enum {
AV_CRC_8_ATM,
AV_CRC_16_ANSI,
AV_CRC_16_CCITT,
AV_CRC_32_IEEE,
AV_CRC_32_IEEE_LE, /*< reversed bitorder version of AV_CRC_32_IEEE */
AV_CRC_16_ANSI_LE, /*< reversed bitorder version of AV_CRC_16_ANSI */
AV_CRC_24_IEEE,
AV_CRC_8_EBU,
AV_CRC_MAX, /*< Not part of public API! Do not use outside libavutil. */
}AVCRCId;
/**
* Initialize a CRC table.
* @param ctx must be an array of size sizeof(AVCRC)*257 or sizeof(AVCRC)*1024
* @param le If 1, the lowest bit represents the coefficient for the highest
* exponent of the corresponding polynomial (both for poly and
* actual CRC).
* If 0, you must swap the CRC parameter and the result of av_crc
* if you need the standard representation (can be simplified in
* most cases to e.g. bswap16):
* av_bswap32(crc << (32-bits))
* @param bits number of bits for the CRC
* @param poly generator polynomial without the x**bits coefficient, in the
* representation as specified by le
* @param ctx_size size of ctx in bytes
* @return <0 on failure
*/
int av_crc_init(AVCRC *ctx, int le, int bits, uint32_t poly, int ctx_size);
/**
* Get an initialized standard CRC table.
* @param crc_id ID of a standard CRC
* @return a pointer to the CRC table or NULL on failure
*/
const AVCRC *av_crc_get_table(AVCRCId crc_id);
/**
* Calculate the CRC of a block.
* @param ctx initialized AVCRC array (see av_crc_init())
* @param crc CRC of previous blocks if any or initial value for CRC
* @param buffer buffer whose CRC to calculate
* @param length length of the buffer
* @return CRC updated with the data from the given block
*
* @see av_crc_init() "le" parameter
*/
uint32_t av_crc(const AVCRC *ctx, uint32_t crc,
const uint8_t *buffer, size_t length) av_pure;
/**
* @}
*/
#endif /* AVUTIL_CRC_H */

View File

@ -0,0 +1,70 @@
/*
* 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
*/
#ifndef AVUTIL_DYNARRAY_H
#define AVUTIL_DYNARRAY_H
#include "log.h"
#include "mem.h"
/**
* Add an element to a dynamic array.
*
* The array is reallocated when its number of elements reaches powers of 2.
* Therefore, the amortized cost of adding an element is constant.
*
* In case of success, the pointer to the array is updated in order to
* point to the new grown array, and the size is incremented.
*
* @param av_size_max maximum size of the array, usually the MAX macro of
* the type of the size
* @param av_elt_size size of the elements in the array, in bytes
* @param av_array pointer to the array, must be a lvalue
* @param av_size size of the array, must be an integer lvalue
* @param av_success statement to execute on success; at this point, the
* size variable is not yet incremented
* @param av_failure statement to execute on failure; if this happens, the
* array and size are not changed; the statement can end
* with a return or a goto
*/
#define FF_DYNARRAY_ADD(av_size_max, av_elt_size, av_array, av_size, \
av_success, av_failure) \
do { \
size_t av_size_new = (av_size); \
if (!((av_size) & ((av_size) - 1))) { \
av_size_new = (av_size) ? (av_size) << 1 : 1; \
if (av_size_new > (av_size_max) / (av_elt_size)) { \
av_size_new = 0; \
} else { \
void *av_array_new = \
av_realloc((av_array), av_size_new * (av_elt_size)); \
if (!av_array_new) \
av_size_new = 0; \
else \
(av_array) = av_array_new; \
} \
} \
if (av_size_new) { \
{ av_success } \
(av_size)++; \
} else { \
av_failure \
} \
} while (0)
#endif /* AVUTIL_DYNARRAY_H */

View File

@ -0,0 +1,128 @@
/*
* 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
* error code definitions
*/
#ifndef AVUTIL_ERROR_H
#define AVUTIL_ERROR_H
#include <errno.h>
#include <stddef.h>
#include "macros.h"
/**
* @addtogroup lavu_error
*
* @{
*/
/* error handling */
#if EDOM > 0
#define AVERROR(e) (-(e)) ///< Returns a negative error code from a POSIX error code, to return from library functions.
#define AVUNERROR(e) (-(e)) ///< Returns a POSIX error code from a library function error return value.
#else
/* Some platforms have E* and errno already negated. */
#define AVERROR(e) (e)
#define AVUNERROR(e) (e)
#endif
#define FFERRTAG(a, b, c, d) (-(int)MKTAG(a, b, c, d))
#define AVERROR_BSF_NOT_FOUND FFERRTAG(0xF8,'B','S','F') ///< Bitstream filter not found
#define AVERROR_BUG FFERRTAG( 'B','U','G','!') ///< Internal bug, also see AVERROR_BUG2
#define AVERROR_BUFFER_TOO_SMALL FFERRTAG( 'B','U','F','S') ///< Buffer too small
#define AVERROR_DECODER_NOT_FOUND FFERRTAG(0xF8,'D','E','C') ///< Decoder not found
#define AVERROR_DEMUXER_NOT_FOUND FFERRTAG(0xF8,'D','E','M') ///< Demuxer not found
#define AVERROR_ENCODER_NOT_FOUND FFERRTAG(0xF8,'E','N','C') ///< Encoder not found
#define AVERROR_EOF FFERRTAG( 'E','O','F',' ') ///< End of file
#define AVERROR_EXIT FFERRTAG( 'E','X','I','T') ///< Immediate exit was requested; the called function should not be restarted
#define AVERROR_EXTERNAL FFERRTAG( 'E','X','T',' ') ///< Generic error in an external library
#define AVERROR_FILTER_NOT_FOUND FFERRTAG(0xF8,'F','I','L') ///< Filter not found
#define AVERROR_INVALIDDATA FFERRTAG( 'I','N','D','A') ///< Invalid data found when processing input
#define AVERROR_MUXER_NOT_FOUND FFERRTAG(0xF8,'M','U','X') ///< Muxer not found
#define AVERROR_OPTION_NOT_FOUND FFERRTAG(0xF8,'O','P','T') ///< Option not found
#define AVERROR_PATCHWELCOME FFERRTAG( 'P','A','W','E') ///< Not yet implemented in FFmpeg, patches welcome
#define AVERROR_PROTOCOL_NOT_FOUND FFERRTAG(0xF8,'P','R','O') ///< Protocol not found
#define AVERROR_STREAM_NOT_FOUND FFERRTAG(0xF8,'S','T','R') ///< Stream not found
/**
* This is semantically identical to AVERROR_BUG
* it has been introduced in Libav after our AVERROR_BUG and with a modified value.
*/
#define AVERROR_BUG2 FFERRTAG( 'B','U','G',' ')
#define AVERROR_UNKNOWN FFERRTAG( 'U','N','K','N') ///< Unknown error, typically from an external library
#define AVERROR_EXPERIMENTAL (-0x2bb2afa8) ///< Requested feature is flagged experimental. Set strict_std_compliance if you really want to use it.
#define AVERROR_INPUT_CHANGED (-0x636e6701) ///< Input changed between calls. Reconfiguration is required. (can be OR-ed with AVERROR_OUTPUT_CHANGED)
#define AVERROR_OUTPUT_CHANGED (-0x636e6702) ///< Output changed between calls. Reconfiguration is required. (can be OR-ed with AVERROR_INPUT_CHANGED)
/* HTTP & RTSP errors */
#define AVERROR_HTTP_BAD_REQUEST FFERRTAG(0xF8,'4','0','0')
#define AVERROR_HTTP_UNAUTHORIZED FFERRTAG(0xF8,'4','0','1')
#define AVERROR_HTTP_FORBIDDEN FFERRTAG(0xF8,'4','0','3')
#define AVERROR_HTTP_NOT_FOUND FFERRTAG(0xF8,'4','0','4')
#define AVERROR_HTTP_OTHER_4XX FFERRTAG(0xF8,'4','X','X')
#define AVERROR_HTTP_SERVER_ERROR FFERRTAG(0xF8,'5','X','X')
#define AV_ERROR_MAX_STRING_SIZE 64
/**
* Put a description of the AVERROR code errnum in errbuf.
* In case of failure the global variable errno is set to indicate the
* error. Even in case of failure av_strerror() will print a generic
* error message indicating the errnum provided to errbuf.
*
* @param errnum error code to describe
* @param errbuf buffer to which description is written
* @param errbuf_size the size in bytes of errbuf
* @return 0 on success, a negative value if a description for errnum
* cannot be found
*/
int av_strerror(int errnum, char *errbuf, size_t errbuf_size);
/**
* Fill the provided buffer with a string containing an error string
* corresponding to the AVERROR code errnum.
*
* @param errbuf a buffer
* @param errbuf_size size in bytes of errbuf
* @param errnum error code to describe
* @return the buffer in input, filled with the error description
* @see av_strerror()
*/
static inline char *av_make_error_string(char *errbuf, size_t errbuf_size, int errnum)
{
av_strerror(errnum, errbuf, errbuf_size);
return errbuf;
}
/**
* Convenience macro, the return value should be used only directly in
* function arguments but never stand-alone.
*/
#define av_err2str(errnum) \
av_make_error_string((char[AV_ERROR_MAX_STRING_SIZE]){0}, AV_ERROR_MAX_STRING_SIZE, errnum)
/**
* @}
*/
#endif /* AVUTIL_ERROR_H */

View File

@ -0,0 +1,189 @@
/*
* copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
*
* 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
* common internal API header
*/
#ifndef AVUTIL_INTERNAL_H
#define AVUTIL_INTERNAL_H
#if !defined(DEBUG) && !defined(NDEBUG)
# define NDEBUG
#endif
// This can be enabled to allow detection of additional integer overflows with ubsan
//#define CHECKED
#include <limits.h>
#include <stdint.h>
#include <stddef.h>
#include <assert.h>
#include <stdio.h>
#include "config.h"
#include "attributes.h"
#include "macros.h"
#include "pixfmt.h"
#if ARCH_X86
# include "x86/emms.h"
#endif
#ifndef emms_c
# define emms_c() do {} while(0)
#endif
#ifndef attribute_align_arg
#if ARCH_X86_32 && AV_GCC_VERSION_AT_LEAST(4,2)
# define attribute_align_arg __attribute__((force_align_arg_pointer))
#else
# define attribute_align_arg
#endif
#endif
#if defined(_WIN32) && CONFIG_SHARED && !defined(BUILDING_avutil)
# define av_export_avutil __declspec(dllimport)
#else
# define av_export_avutil
#endif
#if HAVE_PRAGMA_DEPRECATED
# if defined(__ICL) || defined (__INTEL_COMPILER)
# define FF_DISABLE_DEPRECATION_WARNINGS __pragma(warning(push)) __pragma(warning(disable:1478))
# define FF_ENABLE_DEPRECATION_WARNINGS __pragma(warning(pop))
# elif defined(_MSC_VER)
# define FF_DISABLE_DEPRECATION_WARNINGS __pragma(warning(push)) __pragma(warning(disable:4996))
# define FF_ENABLE_DEPRECATION_WARNINGS __pragma(warning(pop))
# else
# define FF_DISABLE_DEPRECATION_WARNINGS _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
# define FF_ENABLE_DEPRECATION_WARNINGS _Pragma("GCC diagnostic pop")
# endif
#else
# define FF_DISABLE_DEPRECATION_WARNINGS
# define FF_ENABLE_DEPRECATION_WARNINGS
#endif
#define FF_MEMORY_POISON 0x2a
/* Check if the hard coded offset of a struct member still matches reality.
* Induce a compilation failure if not.
*/
#define AV_CHECK_OFFSET(s, m, o) struct check_##o { \
int x_##o[offsetof(s, m) == o? 1: -1]; \
}
#define FF_ALLOC_TYPED_ARRAY(p, nelem) (p = av_malloc_array(nelem, sizeof(*p)))
#define FF_ALLOCZ_TYPED_ARRAY(p, nelem) (p = av_calloc(nelem, sizeof(*p)))
#define FF_PTR_ADD(ptr, off) ((off) ? (ptr) + (off) : (ptr))
/**
* Access a field in a structure by its offset.
*/
#define FF_FIELD_AT(type, off, obj) (*(type *)((char *)&(obj) + (off)))
#include "libm.h"
/**
* Return NULL if CONFIG_SMALL is true, otherwise the argument
* without modification. Used to disable the definition of strings.
*/
#if CONFIG_SMALL
# define NULL_IF_CONFIG_SMALL(x) NULL
#else
# define NULL_IF_CONFIG_SMALL(x) x
#endif
/**
* Log a generic warning message about a missing feature.
*
* @param[in] avc a pointer to an arbitrary struct of which the first
* field is a pointer to an AVClass struct
* @param[in] msg string containing the name of the missing feature
*/
void avpriv_report_missing_feature(void *avc,
const char *msg, ...) av_printf_format(2, 3);
/**
* Log a generic warning message about a missing feature.
* Additionally request that a sample showcasing the feature be uploaded.
*
* @param[in] avc a pointer to an arbitrary struct of which the first field is
* a pointer to an AVClass struct
* @param[in] msg string containing the name of the missing feature
*/
void avpriv_request_sample(void *avc,
const char *msg, ...) av_printf_format(2, 3);
#if HAVE_LIBC_MSVCRT
#include <crtversion.h>
#if defined(_VC_CRT_MAJOR_VERSION) && _VC_CRT_MAJOR_VERSION < 14
#pragma comment(linker, "/include:" EXTERN_PREFIX "avpriv_strtod")
#pragma comment(linker, "/include:" EXTERN_PREFIX "avpriv_snprintf")
#endif
#define PTRDIFF_SPECIFIER "Id"
#define SIZE_SPECIFIER "Iu"
#else
#define PTRDIFF_SPECIFIER "td"
#define SIZE_SPECIFIER "zu"
#endif
#ifdef DEBUG
# define ff_dlog(ctx, ...) av_log(ctx, AV_LOG_DEBUG, __VA_ARGS__)
#else
# define ff_dlog(ctx, ...) do { if (0) av_log(ctx, AV_LOG_DEBUG, __VA_ARGS__); } while (0)
#endif
#ifdef TRACE
# define ff_tlog(ctx, ...) av_log(ctx, AV_LOG_TRACE, __VA_ARGS__)
#else
# define ff_tlog(ctx, ...) do { } while(0)
#endif
// For debuging we use signed operations so overflows can be detected (by ubsan)
// For production we use unsigned so there are no undefined operations
#ifdef CHECKED
#define SUINT int
#define SUINT32 int32_t
#else
#define SUINT unsigned
#define SUINT32 uint32_t
#endif
int avpriv_set_systematic_pal2(uint32_t pal[256], enum AVPixelFormat pix_fmt);
static av_always_inline av_const int avpriv_mirror(int x, int w)
{
if (!w)
return 0;
while ((unsigned)x > (unsigned)w) {
x = -x;
if (x < 0)
x += 2 * w;
}
return x;
}
#endif /* AVUTIL_INTERNAL_H */

View File

@ -0,0 +1,77 @@
/*
* Copyright (c) 2011 Mans Rullgard
*
* 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
*/
#ifndef AVUTIL_INTFLOAT_H
#define AVUTIL_INTFLOAT_H
#include <stdint.h>
#include "attributes.h"
union av_intfloat32 {
uint32_t i;
float f;
};
union av_intfloat64 {
uint64_t i;
double f;
};
/**
* Reinterpret a 32-bit integer as a float.
*/
static av_always_inline float av_int2float(uint32_t i)
{
union av_intfloat32 v;
v.i = i;
return v.f;
}
/**
* Reinterpret a float as a 32-bit integer.
*/
static av_always_inline uint32_t av_float2int(float f)
{
union av_intfloat32 v;
v.f = f;
return v.i;
}
/**
* Reinterpret a 64-bit integer as a double.
*/
static av_always_inline double av_int2double(uint64_t i)
{
union av_intfloat64 v;
v.i = i;
return v.f;
}
/**
* Reinterpret a double as a 64-bit integer.
*/
static av_always_inline uint64_t av_double2int(double f)
{
union av_intfloat64 v;
v.f = f;
return v.i;
}
#endif /* AVUTIL_INTFLOAT_H */

View File

@ -0,0 +1,644 @@
/*
* 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
*/
#ifndef AVUTIL_INTREADWRITE_H
#define AVUTIL_INTREADWRITE_H
#include <stdint.h>
#include "libavutil/avconfig.h"
#include "attributes.h"
#include "bswap.h"
typedef union {
uint64_t u64;
uint32_t u32[2];
uint16_t u16[4];
uint8_t u8 [8];
double f64;
float f32[2];
} av_alias av_alias64;
typedef union {
uint32_t u32;
uint16_t u16[2];
uint8_t u8 [4];
float f32;
} av_alias av_alias32;
typedef union {
uint16_t u16;
uint8_t u8 [2];
} av_alias av_alias16;
/*
* Arch-specific headers can provide any combination of
* AV_[RW][BLN](16|24|32|48|64) and AV_(COPY|SWAP|ZERO)(64|128) macros.
* Preprocessor symbols must be defined, even if these are implemented
* as inline functions.
*
* R/W means read/write, B/L/N means big/little/native endianness.
* The following macros require aligned access, compared to their
* unaligned variants: AV_(COPY|SWAP|ZERO)(64|128), AV_[RW]N[8-64]A.
* Incorrect usage may range from abysmal performance to crash
* depending on the platform.
*
* The unaligned variants are AV_[RW][BLN][8-64] and AV_COPY*U.
*/
#ifdef HAVE_AV_CONFIG_H
#include "config.h"
#if ARCH_ARM
# include "arm/intreadwrite.h"
#elif ARCH_AVR32
# include "avr32/intreadwrite.h"
#elif ARCH_MIPS
# include "mips/intreadwrite.h"
#elif ARCH_PPC
# include "ppc/intreadwrite.h"
#elif ARCH_TOMI
# include "tomi/intreadwrite.h"
#elif ARCH_X86
# include "x86/intreadwrite.h"
#endif
#endif /* HAVE_AV_CONFIG_H */
/*
* Map AV_RNXX <-> AV_R[BL]XX for all variants provided by per-arch headers.
*/
#if AV_HAVE_BIGENDIAN
# if defined(AV_RN16) && !defined(AV_RB16)
# define AV_RB16(p) AV_RN16(p)
# elif !defined(AV_RN16) && defined(AV_RB16)
# define AV_RN16(p) AV_RB16(p)
# endif
# if defined(AV_WN16) && !defined(AV_WB16)
# define AV_WB16(p, v) AV_WN16(p, v)
# elif !defined(AV_WN16) && defined(AV_WB16)
# define AV_WN16(p, v) AV_WB16(p, v)
# endif
# if defined(AV_RN24) && !defined(AV_RB24)
# define AV_RB24(p) AV_RN24(p)
# elif !defined(AV_RN24) && defined(AV_RB24)
# define AV_RN24(p) AV_RB24(p)
# endif
# if defined(AV_WN24) && !defined(AV_WB24)
# define AV_WB24(p, v) AV_WN24(p, v)
# elif !defined(AV_WN24) && defined(AV_WB24)
# define AV_WN24(p, v) AV_WB24(p, v)
# endif
# if defined(AV_RN32) && !defined(AV_RB32)
# define AV_RB32(p) AV_RN32(p)
# elif !defined(AV_RN32) && defined(AV_RB32)
# define AV_RN32(p) AV_RB32(p)
# endif
# if defined(AV_WN32) && !defined(AV_WB32)
# define AV_WB32(p, v) AV_WN32(p, v)
# elif !defined(AV_WN32) && defined(AV_WB32)
# define AV_WN32(p, v) AV_WB32(p, v)
# endif
# if defined(AV_RN48) && !defined(AV_RB48)
# define AV_RB48(p) AV_RN48(p)
# elif !defined(AV_RN48) && defined(AV_RB48)
# define AV_RN48(p) AV_RB48(p)
# endif
# if defined(AV_WN48) && !defined(AV_WB48)
# define AV_WB48(p, v) AV_WN48(p, v)
# elif !defined(AV_WN48) && defined(AV_WB48)
# define AV_WN48(p, v) AV_WB48(p, v)
# endif
# if defined(AV_RN64) && !defined(AV_RB64)
# define AV_RB64(p) AV_RN64(p)
# elif !defined(AV_RN64) && defined(AV_RB64)
# define AV_RN64(p) AV_RB64(p)
# endif
# if defined(AV_WN64) && !defined(AV_WB64)
# define AV_WB64(p, v) AV_WN64(p, v)
# elif !defined(AV_WN64) && defined(AV_WB64)
# define AV_WN64(p, v) AV_WB64(p, v)
# endif
#else /* AV_HAVE_BIGENDIAN */
# if defined(AV_RN16) && !defined(AV_RL16)
# define AV_RL16(p) AV_RN16(p)
# elif !defined(AV_RN16) && defined(AV_RL16)
# define AV_RN16(p) AV_RL16(p)
# endif
# if defined(AV_WN16) && !defined(AV_WL16)
# define AV_WL16(p, v) AV_WN16(p, v)
# elif !defined(AV_WN16) && defined(AV_WL16)
# define AV_WN16(p, v) AV_WL16(p, v)
# endif
# if defined(AV_RN24) && !defined(AV_RL24)
# define AV_RL24(p) AV_RN24(p)
# elif !defined(AV_RN24) && defined(AV_RL24)
# define AV_RN24(p) AV_RL24(p)
# endif
# if defined(AV_WN24) && !defined(AV_WL24)
# define AV_WL24(p, v) AV_WN24(p, v)
# elif !defined(AV_WN24) && defined(AV_WL24)
# define AV_WN24(p, v) AV_WL24(p, v)
# endif
# if defined(AV_RN32) && !defined(AV_RL32)
# define AV_RL32(p) AV_RN32(p)
# elif !defined(AV_RN32) && defined(AV_RL32)
# define AV_RN32(p) AV_RL32(p)
# endif
# if defined(AV_WN32) && !defined(AV_WL32)
# define AV_WL32(p, v) AV_WN32(p, v)
# elif !defined(AV_WN32) && defined(AV_WL32)
# define AV_WN32(p, v) AV_WL32(p, v)
# endif
# if defined(AV_RN48) && !defined(AV_RL48)
# define AV_RL48(p) AV_RN48(p)
# elif !defined(AV_RN48) && defined(AV_RL48)
# define AV_RN48(p) AV_RL48(p)
# endif
# if defined(AV_WN48) && !defined(AV_WL48)
# define AV_WL48(p, v) AV_WN48(p, v)
# elif !defined(AV_WN48) && defined(AV_WL48)
# define AV_WN48(p, v) AV_WL48(p, v)
# endif
# if defined(AV_RN64) && !defined(AV_RL64)
# define AV_RL64(p) AV_RN64(p)
# elif !defined(AV_RN64) && defined(AV_RL64)
# define AV_RN64(p) AV_RL64(p)
# endif
# if defined(AV_WN64) && !defined(AV_WL64)
# define AV_WL64(p, v) AV_WN64(p, v)
# elif !defined(AV_WN64) && defined(AV_WL64)
# define AV_WN64(p, v) AV_WL64(p, v)
# endif
#endif /* !AV_HAVE_BIGENDIAN */
/*
* Define AV_[RW]N helper macros to simplify definitions not provided
* by per-arch headers.
*/
#if defined(__GNUC__)
union unaligned_64 { uint64_t l; } __attribute__((packed)) av_alias;
union unaligned_32 { uint32_t l; } __attribute__((packed)) av_alias;
union unaligned_16 { uint16_t l; } __attribute__((packed)) av_alias;
# define AV_RN(s, p) (((const union unaligned_##s *) (p))->l)
# define AV_WN(s, p, v) ((((union unaligned_##s *) (p))->l) = (v))
#elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_X64) || defined(_M_ARM64)) && AV_HAVE_FAST_UNALIGNED
# define AV_RN(s, p) (*((const __unaligned uint##s##_t*)(p)))
# define AV_WN(s, p, v) (*((__unaligned uint##s##_t*)(p)) = (v))
#elif AV_HAVE_FAST_UNALIGNED
# define AV_RN(s, p) (((const av_alias##s*)(p))->u##s)
# define AV_WN(s, p, v) (((av_alias##s*)(p))->u##s = (v))
#else
#ifndef AV_RB16
# define AV_RB16(x) \
((((const uint8_t*)(x))[0] << 8) | \
((const uint8_t*)(x))[1])
#endif
#ifndef AV_WB16
# define AV_WB16(p, val) do { \
uint16_t d = (val); \
((uint8_t*)(p))[1] = (d); \
((uint8_t*)(p))[0] = (d)>>8; \
} while(0)
#endif
#ifndef AV_RL16
# define AV_RL16(x) \
((((const uint8_t*)(x))[1] << 8) | \
((const uint8_t*)(x))[0])
#endif
#ifndef AV_WL16
# define AV_WL16(p, val) do { \
uint16_t d = (val); \
((uint8_t*)(p))[0] = (d); \
((uint8_t*)(p))[1] = (d)>>8; \
} while(0)
#endif
#ifndef AV_RB32
# define AV_RB32(x) \
(((uint32_t)((const uint8_t*)(x))[0] << 24) | \
(((const uint8_t*)(x))[1] << 16) | \
(((const uint8_t*)(x))[2] << 8) | \
((const uint8_t*)(x))[3])
#endif
#ifndef AV_WB32
# define AV_WB32(p, val) do { \
uint32_t d = (val); \
((uint8_t*)(p))[3] = (d); \
((uint8_t*)(p))[2] = (d)>>8; \
((uint8_t*)(p))[1] = (d)>>16; \
((uint8_t*)(p))[0] = (d)>>24; \
} while(0)
#endif
#ifndef AV_RL32
# define AV_RL32(x) \
(((uint32_t)((const uint8_t*)(x))[3] << 24) | \
(((const uint8_t*)(x))[2] << 16) | \
(((const uint8_t*)(x))[1] << 8) | \
((const uint8_t*)(x))[0])
#endif
#ifndef AV_WL32
# define AV_WL32(p, val) do { \
uint32_t d = (val); \
((uint8_t*)(p))[0] = (d); \
((uint8_t*)(p))[1] = (d)>>8; \
((uint8_t*)(p))[2] = (d)>>16; \
((uint8_t*)(p))[3] = (d)>>24; \
} while(0)
#endif
#ifndef AV_RB64
# define AV_RB64(x) \
(((uint64_t)((const uint8_t*)(x))[0] << 56) | \
((uint64_t)((const uint8_t*)(x))[1] << 48) | \
((uint64_t)((const uint8_t*)(x))[2] << 40) | \
((uint64_t)((const uint8_t*)(x))[3] << 32) | \
((uint64_t)((const uint8_t*)(x))[4] << 24) | \
((uint64_t)((const uint8_t*)(x))[5] << 16) | \
((uint64_t)((const uint8_t*)(x))[6] << 8) | \
(uint64_t)((const uint8_t*)(x))[7])
#endif
#ifndef AV_WB64
# define AV_WB64(p, val) do { \
uint64_t d = (val); \
((uint8_t*)(p))[7] = (d); \
((uint8_t*)(p))[6] = (d)>>8; \
((uint8_t*)(p))[5] = (d)>>16; \
((uint8_t*)(p))[4] = (d)>>24; \
((uint8_t*)(p))[3] = (d)>>32; \
((uint8_t*)(p))[2] = (d)>>40; \
((uint8_t*)(p))[1] = (d)>>48; \
((uint8_t*)(p))[0] = (d)>>56; \
} while(0)
#endif
#ifndef AV_RL64
# define AV_RL64(x) \
(((uint64_t)((const uint8_t*)(x))[7] << 56) | \
((uint64_t)((const uint8_t*)(x))[6] << 48) | \
((uint64_t)((const uint8_t*)(x))[5] << 40) | \
((uint64_t)((const uint8_t*)(x))[4] << 32) | \
((uint64_t)((const uint8_t*)(x))[3] << 24) | \
((uint64_t)((const uint8_t*)(x))[2] << 16) | \
((uint64_t)((const uint8_t*)(x))[1] << 8) | \
(uint64_t)((const uint8_t*)(x))[0])
#endif
#ifndef AV_WL64
# define AV_WL64(p, val) do { \
uint64_t d = (val); \
((uint8_t*)(p))[0] = (d); \
((uint8_t*)(p))[1] = (d)>>8; \
((uint8_t*)(p))[2] = (d)>>16; \
((uint8_t*)(p))[3] = (d)>>24; \
((uint8_t*)(p))[4] = (d)>>32; \
((uint8_t*)(p))[5] = (d)>>40; \
((uint8_t*)(p))[6] = (d)>>48; \
((uint8_t*)(p))[7] = (d)>>56; \
} while(0)
#endif
#if AV_HAVE_BIGENDIAN
# define AV_RN(s, p) AV_RB##s(p)
# define AV_WN(s, p, v) AV_WB##s(p, v)
#else
# define AV_RN(s, p) AV_RL##s(p)
# define AV_WN(s, p, v) AV_WL##s(p, v)
#endif
#endif /* HAVE_FAST_UNALIGNED */
#ifndef AV_RN16
# define AV_RN16(p) AV_RN(16, p)
#endif
#ifndef AV_RN32
# define AV_RN32(p) AV_RN(32, p)
#endif
#ifndef AV_RN64
# define AV_RN64(p) AV_RN(64, p)
#endif
#ifndef AV_WN16
# define AV_WN16(p, v) AV_WN(16, p, v)
#endif
#ifndef AV_WN32
# define AV_WN32(p, v) AV_WN(32, p, v)
#endif
#ifndef AV_WN64
# define AV_WN64(p, v) AV_WN(64, p, v)
#endif
#if AV_HAVE_BIGENDIAN
# define AV_RB(s, p) AV_RN##s(p)
# define AV_WB(s, p, v) AV_WN##s(p, v)
# define AV_RL(s, p) av_bswap##s(AV_RN##s(p))
# define AV_WL(s, p, v) AV_WN##s(p, av_bswap##s(v))
#else
# define AV_RB(s, p) av_bswap##s(AV_RN##s(p))
# define AV_WB(s, p, v) AV_WN##s(p, av_bswap##s(v))
# define AV_RL(s, p) AV_RN##s(p)
# define AV_WL(s, p, v) AV_WN##s(p, v)
#endif
#define AV_RB8(x) (((const uint8_t*)(x))[0])
#define AV_WB8(p, d) do { ((uint8_t*)(p))[0] = (d); } while(0)
#define AV_RL8(x) AV_RB8(x)
#define AV_WL8(p, d) AV_WB8(p, d)
#ifndef AV_RB16
# define AV_RB16(p) AV_RB(16, p)
#endif
#ifndef AV_WB16
# define AV_WB16(p, v) AV_WB(16, p, v)
#endif
#ifndef AV_RL16
# define AV_RL16(p) AV_RL(16, p)
#endif
#ifndef AV_WL16
# define AV_WL16(p, v) AV_WL(16, p, v)
#endif
#ifndef AV_RB32
# define AV_RB32(p) AV_RB(32, p)
#endif
#ifndef AV_WB32
# define AV_WB32(p, v) AV_WB(32, p, v)
#endif
#ifndef AV_RL32
# define AV_RL32(p) AV_RL(32, p)
#endif
#ifndef AV_WL32
# define AV_WL32(p, v) AV_WL(32, p, v)
#endif
#ifndef AV_RB64
# define AV_RB64(p) AV_RB(64, p)
#endif
#ifndef AV_WB64
# define AV_WB64(p, v) AV_WB(64, p, v)
#endif
#ifndef AV_RL64
# define AV_RL64(p) AV_RL(64, p)
#endif
#ifndef AV_WL64
# define AV_WL64(p, v) AV_WL(64, p, v)
#endif
#ifndef AV_RB24
# define AV_RB24(x) \
((((const uint8_t*)(x))[0] << 16) | \
(((const uint8_t*)(x))[1] << 8) | \
((const uint8_t*)(x))[2])
#endif
#ifndef AV_WB24
# define AV_WB24(p, d) do { \
((uint8_t*)(p))[2] = (d); \
((uint8_t*)(p))[1] = (d)>>8; \
((uint8_t*)(p))[0] = (d)>>16; \
} while(0)
#endif
#ifndef AV_RL24
# define AV_RL24(x) \
((((const uint8_t*)(x))[2] << 16) | \
(((const uint8_t*)(x))[1] << 8) | \
((const uint8_t*)(x))[0])
#endif
#ifndef AV_WL24
# define AV_WL24(p, d) do { \
((uint8_t*)(p))[0] = (d); \
((uint8_t*)(p))[1] = (d)>>8; \
((uint8_t*)(p))[2] = (d)>>16; \
} while(0)
#endif
#ifndef AV_RB48
# define AV_RB48(x) \
(((uint64_t)((const uint8_t*)(x))[0] << 40) | \
((uint64_t)((const uint8_t*)(x))[1] << 32) | \
((uint64_t)((const uint8_t*)(x))[2] << 24) | \
((uint64_t)((const uint8_t*)(x))[3] << 16) | \
((uint64_t)((const uint8_t*)(x))[4] << 8) | \
(uint64_t)((const uint8_t*)(x))[5])
#endif
#ifndef AV_WB48
# define AV_WB48(p, darg) do { \
uint64_t d = (darg); \
((uint8_t*)(p))[5] = (d); \
((uint8_t*)(p))[4] = (d)>>8; \
((uint8_t*)(p))[3] = (d)>>16; \
((uint8_t*)(p))[2] = (d)>>24; \
((uint8_t*)(p))[1] = (d)>>32; \
((uint8_t*)(p))[0] = (d)>>40; \
} while(0)
#endif
#ifndef AV_RL48
# define AV_RL48(x) \
(((uint64_t)((const uint8_t*)(x))[5] << 40) | \
((uint64_t)((const uint8_t*)(x))[4] << 32) | \
((uint64_t)((const uint8_t*)(x))[3] << 24) | \
((uint64_t)((const uint8_t*)(x))[2] << 16) | \
((uint64_t)((const uint8_t*)(x))[1] << 8) | \
(uint64_t)((const uint8_t*)(x))[0])
#endif
#ifndef AV_WL48
# define AV_WL48(p, darg) do { \
uint64_t d = (darg); \
((uint8_t*)(p))[0] = (d); \
((uint8_t*)(p))[1] = (d)>>8; \
((uint8_t*)(p))[2] = (d)>>16; \
((uint8_t*)(p))[3] = (d)>>24; \
((uint8_t*)(p))[4] = (d)>>32; \
((uint8_t*)(p))[5] = (d)>>40; \
} while(0)
#endif
/*
* The AV_[RW]NA macros access naturally aligned data
* in a type-safe way.
*/
#define AV_RNA(s, p) (((const av_alias##s*)(p))->u##s)
#define AV_WNA(s, p, v) (((av_alias##s*)(p))->u##s = (v))
#ifndef AV_RN16A
# define AV_RN16A(p) AV_RNA(16, p)
#endif
#ifndef AV_RN32A
# define AV_RN32A(p) AV_RNA(32, p)
#endif
#ifndef AV_RN64A
# define AV_RN64A(p) AV_RNA(64, p)
#endif
#ifndef AV_WN16A
# define AV_WN16A(p, v) AV_WNA(16, p, v)
#endif
#ifndef AV_WN32A
# define AV_WN32A(p, v) AV_WNA(32, p, v)
#endif
#ifndef AV_WN64A
# define AV_WN64A(p, v) AV_WNA(64, p, v)
#endif
#if AV_HAVE_BIGENDIAN
# define AV_RLA(s, p) av_bswap##s(AV_RN##s##A(p))
# define AV_WLA(s, p, v) AV_WN##s##A(p, av_bswap##s(v))
#else
# define AV_RLA(s, p) AV_RN##s##A(p)
# define AV_WLA(s, p, v) AV_WN##s##A(p, v)
#endif
#ifndef AV_RL64A
# define AV_RL64A(p) AV_RLA(64, p)
#endif
#ifndef AV_WL64A
# define AV_WL64A(p, v) AV_WLA(64, p, v)
#endif
/*
* The AV_COPYxxU macros are suitable for copying data to/from unaligned
* memory locations.
*/
#define AV_COPYU(n, d, s) AV_WN##n(d, AV_RN##n(s));
#ifndef AV_COPY16U
# define AV_COPY16U(d, s) AV_COPYU(16, d, s)
#endif
#ifndef AV_COPY32U
# define AV_COPY32U(d, s) AV_COPYU(32, d, s)
#endif
#ifndef AV_COPY64U
# define AV_COPY64U(d, s) AV_COPYU(64, d, s)
#endif
#ifndef AV_COPY128U
# define AV_COPY128U(d, s) \
do { \
AV_COPY64U(d, s); \
AV_COPY64U((char *)(d) + 8, (const char *)(s) + 8); \
} while(0)
#endif
/* Parameters for AV_COPY*, AV_SWAP*, AV_ZERO* must be
* naturally aligned. They may be implemented using MMX,
* so emms_c() must be called before using any float code
* afterwards.
*/
#define AV_COPY(n, d, s) \
(((av_alias##n*)(d))->u##n = ((const av_alias##n*)(s))->u##n)
#ifndef AV_COPY16
# define AV_COPY16(d, s) AV_COPY(16, d, s)
#endif
#ifndef AV_COPY32
# define AV_COPY32(d, s) AV_COPY(32, d, s)
#endif
#ifndef AV_COPY64
# define AV_COPY64(d, s) AV_COPY(64, d, s)
#endif
#ifndef AV_COPY128
# define AV_COPY128(d, s) \
do { \
AV_COPY64(d, s); \
AV_COPY64((char*)(d)+8, (char*)(s)+8); \
} while(0)
#endif
#define AV_SWAP(n, a, b) FFSWAP(av_alias##n, *(av_alias##n*)(a), *(av_alias##n*)(b))
#ifndef AV_SWAP64
# define AV_SWAP64(a, b) AV_SWAP(64, a, b)
#endif
#define AV_ZERO(n, d) (((av_alias##n*)(d))->u##n = 0)
#ifndef AV_ZERO16
# define AV_ZERO16(d) AV_ZERO(16, d)
#endif
#ifndef AV_ZERO32
# define AV_ZERO32(d) AV_ZERO(32, d)
#endif
#ifndef AV_ZERO64
# define AV_ZERO64(d) AV_ZERO(64, d)
#endif
#ifndef AV_ZERO128
# define AV_ZERO128(d) \
do { \
AV_ZERO64(d); \
AV_ZERO64((char*)(d)+8); \
} while(0)
#endif
#endif /* AVUTIL_INTREADWRITE_H */

View File

@ -0,0 +1,87 @@
/*
* Lagged Fibonacci PRNG
* Copyright (c) 2008 Michael Niedermayer
*
* 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
*/
#include <inttypes.h>
#include <limits.h>
#include <math.h>
#include "lfg.h"
#include "crc.h"
#include "md5.h"
#include "error.h"
#include "intreadwrite.h"
#include "attributes.h"
av_cold void av_lfg_init(AVLFG *c, unsigned int seed)
{
uint8_t tmp[16] = { 0 };
int i;
for (i = 8; i < 64; i += 4) {
AV_WL32(tmp, seed);
tmp[4] = i;
av_md5_sum(tmp, tmp, 16);
c->state[i ] = AV_RL32(tmp);
c->state[i + 1] = AV_RL32(tmp + 4);
c->state[i + 2] = AV_RL32(tmp + 8);
c->state[i + 3] = AV_RL32(tmp + 12);
}
c->index = 0;
}
void av_bmg_get(AVLFG *lfg, double out[2])
{
double x1, x2, w;
do {
x1 = 2.0 / UINT_MAX * av_lfg_get(lfg) - 1.0;
x2 = 2.0 / UINT_MAX * av_lfg_get(lfg) - 1.0;
w = x1 * x1 + x2 * x2;
} while (w >= 1.0);
w = sqrt((-2.0 * log(w)) / w);
out[0] = x1 * w;
out[1] = x2 * w;
}
int av_lfg_init_from_data(AVLFG *c, const uint8_t *data, unsigned int length) {
unsigned int beg, end, segm;
const AVCRC *avcrc;
uint32_t crc = 1;
/* avoid integer overflow in the loop below. */
if (length > (UINT_MAX / 128U)) return AVERROR(EINVAL);
c->index = 0;
avcrc = av_crc_get_table(AV_CRC_32_IEEE); /* This can't fail. It's a well-defined table in crc.c */
/* across 64 segments of the incoming data,
* do a running crc of each segment and store the crc as the state for that slot.
* this works even if the length of the segment is 0 bytes. */
beg = 0;
for (segm = 0;segm < 64;segm++) {
end = (((segm + 1) * length) / 64);
crc = av_crc(avcrc, crc, data + beg, end - beg);
c->state[segm] = (unsigned int)crc;
beg = end;
}
return 0;
}

View File

@ -0,0 +1,81 @@
/*
* Lagged Fibonacci PRNG
* Copyright (c) 2008 Michael Niedermayer
*
* 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
*/
#ifndef AVUTIL_LFG_H
#define AVUTIL_LFG_H
#include <stdint.h>
/**
* Context structure for the Lagged Fibonacci PRNG.
* The exact layout, types and content of this struct may change and should
* not be accessed directly. Only its `sizeof()` is guaranteed to stay the same
* to allow easy instanciation.
*/
typedef struct AVLFG {
unsigned int state[64];
int index;
} AVLFG;
void av_lfg_init(AVLFG *c, unsigned int seed);
/**
* Seed the state of the ALFG using binary data.
*
* @return 0 on success, negative value (AVERROR) on failure.
*/
int av_lfg_init_from_data(AVLFG *c, const uint8_t *data, unsigned int length);
/**
* Get the next random unsigned 32-bit number using an ALFG.
*
* Please also consider a simple LCG like state= state*1664525+1013904223,
* it may be good enough and faster for your specific use case.
*/
static inline unsigned int av_lfg_get(AVLFG *c){
unsigned a = c->state[c->index & 63] = c->state[(c->index-24) & 63] + c->state[(c->index-55) & 63];
c->index += 1U;
return a;
}
/**
* Get the next random unsigned 32-bit number using a MLFG.
*
* Please also consider av_lfg_get() above, it is faster.
*/
static inline unsigned int av_mlfg_get(AVLFG *c){
unsigned int a= c->state[(c->index-55) & 63];
unsigned int b= c->state[(c->index-24) & 63];
a = c->state[c->index & 63] = 2*a*b+a+b;
c->index += 1U;
return a;
}
/**
* Get the next two numbers generated by a Box-Muller Gaussian
* generator using the random numbers issued by lfg.
*
* @param lfg pointer to the contex structure
* @param out array where the two generated numbers are placed
*/
void av_bmg_get(AVLFG *lfg, double out[2]);
#endif /* AVUTIL_LFG_H */

View File

@ -0,0 +1,472 @@
/*
* erf function: Copyright (c) 2006 John Maddock
* 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
* Replacements for frequently missing libm functions
*/
#if 0
//#ifndef AVUTIL_LIBM_H
#define AVUTIL_LIBM_H
#include <math.h>
#include "config.h"
#include "attributes.h"
#include "intfloat.h"
#include "mathematics.h"
#if HAVE_MIPSFPU && HAVE_INLINE_ASM
#include "libavutil/mips/libm_mips.h"
#endif /* HAVE_MIPSFPU && HAVE_INLINE_ASM*/
#if !HAVE_ATANF
#undef atanf
#define atanf(x) ((float)atan(x))
#endif /* HAVE_ATANF */
#if !HAVE_ATAN2F
#undef atan2f
#define atan2f(y, x) ((float)atan2(y, x))
#endif /* HAVE_ATAN2F */
#if !HAVE_POWF
#undef powf
#define powf(x, y) ((float)pow(x, y))
#endif /* HAVE_POWF */
#if !HAVE_CBRT
static av_always_inline double cbrt(double x)
{
return x < 0 ? -pow(-x, 1.0 / 3.0) : pow(x, 1.0 / 3.0);
}
#endif /* HAVE_CBRT */
#if !HAVE_CBRTF
static av_always_inline float cbrtf(float x)
{
return x < 0 ? -powf(-x, 1.0 / 3.0) : powf(x, 1.0 / 3.0);
}
#endif /* HAVE_CBRTF */
#if !HAVE_COPYSIGN
static av_always_inline double copysign(double x, double y)
{
uint64_t vx = av_double2int(x);
uint64_t vy = av_double2int(y);
return av_int2double((vx & UINT64_C(0x7fffffffffffffff)) | (vy & UINT64_C(0x8000000000000000)));
}
#endif /* HAVE_COPYSIGN */
#if !HAVE_COSF
#undef cosf
#define cosf(x) ((float)cos(x))
#endif /* HAVE_COSF */
#if !HAVE_ERF
static inline double ff_eval_poly(const double *coeff, int size, double x) {
double sum = coeff[size-1];
int i;
for (i = size-2; i >= 0; --i) {
sum *= x;
sum += coeff[i];
}
return sum;
}
/**
* erf function
* Algorithm taken from the Boost project, source:
* http://www.boost.org/doc/libs/1_46_1/boost/math/special_functions/erf.hpp
* Use, modification and distribution are subject to the
* Boost Software License, Version 1.0 (see notice below).
* Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
static inline double erf(double z)
{
#ifndef FF_ARRAY_ELEMS
#define FF_ARRAY_ELEMS(a) (sizeof(a) / sizeof((a)[0]))
#endif
double result;
/* handle the symmetry: erf(-x) = -erf(x) */
if (z < 0)
return -erf(-z);
/* branch based on range of z, and pick appropriate approximation */
if (z == 0)
return 0;
else if (z < 1e-10)
return z * 1.125 + z * 0.003379167095512573896158903121545171688;
else if (z < 0.5) {
// Maximum Deviation Found: 1.561e-17
// Expected Error Term: 1.561e-17
// Maximum Relative Change in Control Points: 1.155e-04
// Max Error found at double precision = 2.961182e-17
static const double y = 1.044948577880859375;
static const double p[] = {
0.0834305892146531832907,
-0.338165134459360935041,
-0.0509990735146777432841,
-0.00772758345802133288487,
-0.000322780120964605683831,
};
static const double q[] = {
1,
0.455004033050794024546,
0.0875222600142252549554,
0.00858571925074406212772,
0.000370900071787748000569,
};
double zz = z * z;
return z * (y + ff_eval_poly(p, FF_ARRAY_ELEMS(p), zz) / ff_eval_poly(q, FF_ARRAY_ELEMS(q), zz));
}
/* here onwards compute erfc */
else if (z < 1.5) {
// Maximum Deviation Found: 3.702e-17
// Expected Error Term: 3.702e-17
// Maximum Relative Change in Control Points: 2.845e-04
// Max Error found at double precision = 4.841816e-17
static const double y = 0.405935764312744140625;
static const double p[] = {
-0.098090592216281240205,
0.178114665841120341155,
0.191003695796775433986,
0.0888900368967884466578,
0.0195049001251218801359,
0.00180424538297014223957,
};
static const double q[] = {
1,
1.84759070983002217845,
1.42628004845511324508,
0.578052804889902404909,
0.12385097467900864233,
0.0113385233577001411017,
0.337511472483094676155e-5,
};
result = y + ff_eval_poly(p, FF_ARRAY_ELEMS(p), z - 0.5) / ff_eval_poly(q, FF_ARRAY_ELEMS(q), z - 0.5);
result *= exp(-z * z) / z;
return 1 - result;
}
else if (z < 2.5) {
// Max Error found at double precision = 6.599585e-18
// Maximum Deviation Found: 3.909e-18
// Expected Error Term: 3.909e-18
// Maximum Relative Change in Control Points: 9.886e-05
static const double y = 0.50672817230224609375;
static const double p[] = {
-0.0243500476207698441272,
0.0386540375035707201728,
0.04394818964209516296,
0.0175679436311802092299,
0.00323962406290842133584,
0.000235839115596880717416,
};
static const double q[] = {
1,
1.53991494948552447182,
0.982403709157920235114,
0.325732924782444448493,
0.0563921837420478160373,
0.00410369723978904575884,
};
result = y + ff_eval_poly(p, FF_ARRAY_ELEMS(p), z - 1.5) / ff_eval_poly(q, FF_ARRAY_ELEMS(q), z - 1.5);
result *= exp(-z * z) / z;
return 1 - result;
}
else if (z < 4.5) {
// Maximum Deviation Found: 1.512e-17
// Expected Error Term: 1.512e-17
// Maximum Relative Change in Control Points: 2.222e-04
// Max Error found at double precision = 2.062515e-17
static const double y = 0.5405750274658203125;
static const double p[] = {
0.00295276716530971662634,
0.0137384425896355332126,
0.00840807615555585383007,
0.00212825620914618649141,
0.000250269961544794627958,
0.113212406648847561139e-4,
};
static const double q[] = {
1,
1.04217814166938418171,
0.442597659481563127003,
0.0958492726301061423444,
0.0105982906484876531489,
0.000479411269521714493907,
};
result = y + ff_eval_poly(p, FF_ARRAY_ELEMS(p), z - 3.5) / ff_eval_poly(q, FF_ARRAY_ELEMS(q), z - 3.5);
result *= exp(-z * z) / z;
return 1 - result;
}
/* differ from Boost here, the claim of underflow of erfc(x) past 5.8 is
* slightly incorrect, change to 5.92
* (really somewhere between 5.9125 and 5.925 is when it saturates) */
else if (z < 5.92) {
// Max Error found at double precision = 2.997958e-17
// Maximum Deviation Found: 2.860e-17
// Expected Error Term: 2.859e-17
// Maximum Relative Change in Control Points: 1.357e-05
static const double y = 0.5579090118408203125;
static const double p[] = {
0.00628057170626964891937,
0.0175389834052493308818,
-0.212652252872804219852,
-0.687717681153649930619,
-2.5518551727311523996,
-3.22729451764143718517,
-2.8175401114513378771,
};
static const double q[] = {
1,
2.79257750980575282228,
11.0567237927800161565,
15.930646027911794143,
22.9367376522880577224,
13.5064170191802889145,
5.48409182238641741584,
};
result = y + ff_eval_poly(p, FF_ARRAY_ELEMS(p), 1 / z) / ff_eval_poly(q, FF_ARRAY_ELEMS(q), 1 / z);
result *= exp(-z * z) / z;
return 1 - result;
}
/* handle the nan case, but don't use isnan for max portability */
else if (z != z)
return z;
/* finally return saturated result */
else
return 1;
}
#endif /* HAVE_ERF */
#if !HAVE_EXPF
#undef expf
#define expf(x) ((float)exp(x))
#endif /* HAVE_EXPF */
#if !HAVE_EXP2
#undef exp2
#define exp2(x) exp((x) * M_LN2)
#endif /* HAVE_EXP2 */
#if !HAVE_EXP2F
#undef exp2f
#define exp2f(x) ((float)exp2(x))
#endif /* HAVE_EXP2F */
#if !HAVE_ISINF
#undef isinf
/* Note: these do not follow the BSD/Apple/GNU convention of returning -1 for
-Inf, +1 for Inf, 0 otherwise, but merely follow the POSIX/ISO mandated spec of
returning a non-zero value for +/-Inf, 0 otherwise. */
static av_always_inline av_const int avpriv_isinff(float x)
{
uint32_t v = av_float2int(x);
if ((v & 0x7f800000) != 0x7f800000)
return 0;
return !(v & 0x007fffff);
}
static av_always_inline av_const int avpriv_isinf(double x)
{
uint64_t v = av_double2int(x);
if ((v & 0x7ff0000000000000) != 0x7ff0000000000000)
return 0;
return !(v & 0x000fffffffffffff);
}
#define isinf(x) \
(sizeof(x) == sizeof(float) \
? avpriv_isinff(x) \
: avpriv_isinf(x))
#endif /* HAVE_ISINF */
#if !HAVE_ISNAN
static av_always_inline av_const int avpriv_isnanf(float x)
{
uint32_t v = av_float2int(x);
if ((v & 0x7f800000) != 0x7f800000)
return 0;
return v & 0x007fffff;
}
static av_always_inline av_const int avpriv_isnan(double x)
{
uint64_t v = av_double2int(x);
if ((v & 0x7ff0000000000000) != 0x7ff0000000000000)
return 0;
return (v & 0x000fffffffffffff) && 1;
}
#define isnan(x) \
(sizeof(x) == sizeof(float) \
? avpriv_isnanf(x) \
: avpriv_isnan(x))
#endif /* HAVE_ISNAN */
#if !HAVE_ISFINITE
static av_always_inline av_const int avpriv_isfinitef(float x)
{
uint32_t v = av_float2int(x);
return (v & 0x7f800000) != 0x7f800000;
}
static av_always_inline av_const int avpriv_isfinite(double x)
{
uint64_t v = av_double2int(x);
return (v & 0x7ff0000000000000) != 0x7ff0000000000000;
}
#define isfinite(x) \
(sizeof(x) == sizeof(float) \
? avpriv_isfinitef(x) \
: avpriv_isfinite(x))
#endif /* HAVE_ISFINITE */
#if !HAVE_HYPOT
static inline av_const double hypot(double x, double y)
{
double ret, temp;
x = fabs(x);
y = fabs(y);
if (isinf(x) || isinf(y))
return av_int2double(0x7ff0000000000000);
if (x == 0 || y == 0)
return x + y;
if (x < y) {
temp = x;
x = y;
y = temp;
}
y = y/x;
return x*sqrt(1 + y*y);
}
#endif /* HAVE_HYPOT */
#if !HAVE_LDEXPF
#undef ldexpf
#define ldexpf(x, exp) ((float)ldexp(x, exp))
#endif /* HAVE_LDEXPF */
#if !HAVE_LLRINT
#undef llrint
#define llrint(x) ((long long)rint(x))
#endif /* HAVE_LLRINT */
#if !HAVE_LLRINTF
#undef llrintf
#define llrintf(x) ((long long)rint(x))
#endif /* HAVE_LLRINT */
#if !HAVE_LOG2
#undef log2
#define log2(x) (log(x) * 1.44269504088896340736)
#endif /* HAVE_LOG2 */
#if !HAVE_LOG2F
#undef log2f
#define log2f(x) ((float)log2(x))
#endif /* HAVE_LOG2F */
#if !HAVE_LOG10F
#undef log10f
#define log10f(x) ((float)log10(x))
#endif /* HAVE_LOG10F */
#if !HAVE_SINF
#undef sinf
#define sinf(x) ((float)sin(x))
#endif /* HAVE_SINF */
#if !HAVE_RINT
static inline double rint(double x)
{
return x >= 0 ? floor(x + 0.5) : ceil(x - 0.5);
}
#endif /* HAVE_RINT */
#if !HAVE_LRINT
static av_always_inline av_const long int lrint(double x)
{
return rint(x);
}
#endif /* HAVE_LRINT */
#if !HAVE_LRINTF
static av_always_inline av_const long int lrintf(float x)
{
return (int)(rint(x));
}
#endif /* HAVE_LRINTF */
#if !HAVE_ROUND
static av_always_inline av_const double round(double x)
{
return (x > 0) ? floor(x + 0.5) : ceil(x - 0.5);
}
#endif /* HAVE_ROUND */
#if !HAVE_ROUNDF
static av_always_inline av_const float roundf(float x)
{
return (x > 0) ? floor(x + 0.5) : ceil(x - 0.5);
}
#endif /* HAVE_ROUNDF */
#if !HAVE_TRUNC
static av_always_inline av_const double trunc(double x)
{
return (x > 0) ? floor(x) : ceil(x);
}
#endif /* HAVE_TRUNC */
#if !HAVE_TRUNCF
static av_always_inline av_const float truncf(float x)
{
return (x > 0) ? floor(x) : ceil(x);
}
#endif /* HAVE_TRUNCF */
#endif /* AVUTIL_LIBM_H */

View File

@ -0,0 +1,387 @@
/*
* copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
*
* 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
*/
#ifndef AVUTIL_LOG_H
#define AVUTIL_LOG_H
#include <stdarg.h>
#include "attributes.h"
#include "version.h"
typedef enum {
AV_CLASS_CATEGORY_NA = 0,
AV_CLASS_CATEGORY_INPUT,
AV_CLASS_CATEGORY_OUTPUT,
AV_CLASS_CATEGORY_MUXER,
AV_CLASS_CATEGORY_DEMUXER,
AV_CLASS_CATEGORY_ENCODER,
AV_CLASS_CATEGORY_DECODER,
AV_CLASS_CATEGORY_FILTER,
AV_CLASS_CATEGORY_BITSTREAM_FILTER,
AV_CLASS_CATEGORY_SWSCALER,
AV_CLASS_CATEGORY_SWRESAMPLER,
AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT = 40,
AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT,
AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT,
AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT,
AV_CLASS_CATEGORY_DEVICE_OUTPUT,
AV_CLASS_CATEGORY_DEVICE_INPUT,
AV_CLASS_CATEGORY_NB ///< not part of ABI/API
}AVClassCategory;
#define AV_IS_INPUT_DEVICE(category) \
(((category) == AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT) || \
((category) == AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT) || \
((category) == AV_CLASS_CATEGORY_DEVICE_INPUT))
#define AV_IS_OUTPUT_DEVICE(category) \
(((category) == AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT) || \
((category) == AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT) || \
((category) == AV_CLASS_CATEGORY_DEVICE_OUTPUT))
struct AVOptionRanges;
/**
* Describe the class of an AVClass context structure. That is an
* arbitrary struct of which the first field is a pointer to an
* AVClass struct (e.g. AVCodecContext, AVFormatContext etc.).
*/
typedef struct AVClass {
/**
* The name of the class; usually it is the same name as the
* context structure type to which the AVClass is associated.
*/
const char* class_name;
/**
* A pointer to a function which returns the name of a context
* instance ctx associated with the class.
*/
const char* (*item_name)(void* ctx);
/**
* a pointer to the first option specified in the class if any or NULL
*
* @see av_set_default_options()
*/
const struct AVOption *option;
/**
* LIBAVUTIL_VERSION with which this structure was created.
* This is used to allow fields to be added without requiring major
* version bumps everywhere.
*/
int version;
/**
* Offset in the structure where log_level_offset is stored.
* 0 means there is no such variable
*/
int log_level_offset_offset;
/**
* Offset in the structure where a pointer to the parent context for
* logging is stored. For example a decoder could pass its AVCodecContext
* to eval as such a parent context, which an av_log() implementation
* could then leverage to display the parent context.
* The offset can be NULL.
*/
int parent_log_context_offset;
/**
* Category used for visualization (like color)
* This is only set if the category is equal for all objects using this class.
* available since version (51 << 16 | 56 << 8 | 100)
*/
AVClassCategory category;
/**
* Callback to return the category.
* available since version (51 << 16 | 59 << 8 | 100)
*/
AVClassCategory (*get_category)(void* ctx);
/**
* Callback to return the supported/allowed ranges.
* available since version (52.12)
*/
int (*query_ranges)(struct AVOptionRanges **, void *obj, const char *key, int flags);
/**
* Return next AVOptions-enabled child or NULL
*/
void* (*child_next)(void *obj, void *prev);
/**
* Iterate over the AVClasses corresponding to potential AVOptions-enabled
* children.
*
* @param iter pointer to opaque iteration state. The caller must initialize
* *iter to NULL before the first call.
* @return AVClass for the next AVOptions-enabled child or NULL if there are
* no more such children.
*
* @note The difference between child_next and this is that child_next
* iterates over _already existing_ objects, while child_class_iterate
* iterates over _all possible_ children.
*/
const struct AVClass* (*child_class_iterate)(void **iter);
} AVClass;
/**
* @addtogroup lavu_log
*
* @{
*
* @defgroup lavu_log_constants Logging Constants
*
* @{
*/
/**
* Print no output.
*/
#define AV_LOG_QUIET -8
/**
* Something went really wrong and we will crash now.
*/
#define AV_LOG_PANIC 0
/**
* Something went wrong and recovery is not possible.
* For example, no header was found for a format which depends
* on headers or an illegal combination of parameters is used.
*/
#define AV_LOG_FATAL 8
/**
* Something went wrong and cannot losslessly be recovered.
* However, not all future data is affected.
*/
#define AV_LOG_ERROR 16
/**
* Something somehow does not look correct. This may or may not
* lead to problems. An example would be the use of '-vstrict -2'.
*/
#define AV_LOG_WARNING 24
/**
* Standard information.
*/
#define AV_LOG_INFO 32
/**
* Detailed information.
*/
#define AV_LOG_VERBOSE 40
/**
* Stuff which is only useful for libav* developers.
*/
#define AV_LOG_DEBUG 48
/**
* Extremely verbose debugging, useful for libav* development.
*/
#define AV_LOG_TRACE 56
#define AV_LOG_MAX_OFFSET (AV_LOG_TRACE - AV_LOG_QUIET)
/**
* @}
*/
/**
* Sets additional colors for extended debugging sessions.
* @code
av_log(ctx, AV_LOG_DEBUG|AV_LOG_C(134), "Message in purple\n");
@endcode
* Requires 256color terminal support. Uses outside debugging is not
* recommended.
*/
#define AV_LOG_C(x) ((x) << 8)
/**
* Send the specified message to the log if the level is less than or equal
* to the current av_log_level. By default, all logging messages are sent to
* stderr. This behavior can be altered by setting a different logging callback
* function.
* @see av_log_set_callback
*
* @param avcl A pointer to an arbitrary struct of which the first field is a
* pointer to an AVClass struct or NULL if general log.
* @param level The importance level of the message expressed using a @ref
* lavu_log_constants "Logging Constant".
* @param fmt The format string (printf-compatible) that specifies how
* subsequent arguments are converted to output.
*/
void av_log(void *avcl, int level, const char *fmt, ...) av_printf_format(3, 4);
/**
* Send the specified message to the log once with the initial_level and then with
* the subsequent_level. By default, all logging messages are sent to
* stderr. This behavior can be altered by setting a different logging callback
* function.
* @see av_log
*
* @param avcl A pointer to an arbitrary struct of which the first field is a
* pointer to an AVClass struct or NULL if general log.
* @param initial_level importance level of the message expressed using a @ref
* lavu_log_constants "Logging Constant" for the first occurance.
* @param subsequent_level importance level of the message expressed using a @ref
* lavu_log_constants "Logging Constant" after the first occurance.
* @param fmt The format string (printf-compatible) that specifies how
* subsequent arguments are converted to output.
* @param state a variable to keep trak of if a message has already been printed
* this must be initialized to 0 before the first use. The same state
* must not be accessed by 2 Threads simultaneously.
*/
void av_log_once(void* avcl, int initial_level, int subsequent_level, int *state, const char *fmt, ...) av_printf_format(5, 6);
/**
* Send the specified message to the log if the level is less than or equal
* to the current av_log_level. By default, all logging messages are sent to
* stderr. This behavior can be altered by setting a different logging callback
* function.
* @see av_log_set_callback
*
* @param avcl A pointer to an arbitrary struct of which the first field is a
* pointer to an AVClass struct.
* @param level The importance level of the message expressed using a @ref
* lavu_log_constants "Logging Constant".
* @param fmt The format string (printf-compatible) that specifies how
* subsequent arguments are converted to output.
* @param vl The arguments referenced by the format string.
*/
void av_vlog(void *avcl, int level, const char *fmt, va_list vl);
/**
* Get the current log level
*
* @see lavu_log_constants
*
* @return Current log level
*/
int av_log_get_level(void);
/**
* Set the log level
*
* @see lavu_log_constants
*
* @param level Logging level
*/
void av_log_set_level(int level);
/**
* Set the logging callback
*
* @note The callback must be thread safe, even if the application does not use
* threads itself as some codecs are multithreaded.
*
* @see av_log_default_callback
*
* @param callback A logging function with a compatible signature.
*/
void av_log_set_callback(void (*callback)(void*, int, const char*, va_list));
/**
* Default logging callback
*
* It prints the message to stderr, optionally colorizing it.
*
* @param avcl A pointer to an arbitrary struct of which the first field is a
* pointer to an AVClass struct.
* @param level The importance level of the message expressed using a @ref
* lavu_log_constants "Logging Constant".
* @param fmt The format string (printf-compatible) that specifies how
* subsequent arguments are converted to output.
* @param vl The arguments referenced by the format string.
*/
void av_log_default_callback(void *avcl, int level, const char *fmt,
va_list vl);
/**
* Return the context name
*
* @param ctx The AVClass context
*
* @return The AVClass class_name
*/
const char* av_default_item_name(void* ctx);
AVClassCategory av_default_get_category(void *ptr);
/**
* Format a line of log the same way as the default callback.
* @param line buffer to receive the formatted line
* @param line_size size of the buffer
* @param print_prefix used to store whether the prefix must be printed;
* must point to a persistent integer initially set to 1
*/
void av_log_format_line(void *ptr, int level, const char *fmt, va_list vl,
char *line, int line_size, int *print_prefix);
/**
* Format a line of log the same way as the default callback.
* @param line buffer to receive the formatted line;
* may be NULL if line_size is 0
* @param line_size size of the buffer; at most line_size-1 characters will
* be written to the buffer, plus one null terminator
* @param print_prefix used to store whether the prefix must be printed;
* must point to a persistent integer initially set to 1
* @return Returns a negative value if an error occurred, otherwise returns
* the number of characters that would have been written for a
* sufficiently large buffer, not including the terminating null
* character. If the return value is not less than line_size, it means
* that the log message was truncated to fit the buffer.
*/
int av_log_format_line2(void *ptr, int level, const char *fmt, va_list vl,
char *line, int line_size, int *print_prefix);
/**
* Skip repeated messages, this requires the user app to use av_log() instead of
* (f)printf as the 2 would otherwise interfere and lead to
* "Last message repeated x times" messages below (f)printf messages with some
* bad luck.
* Also to receive the last, "last repeated" line if any, the user app must
* call av_log(NULL, AV_LOG_QUIET, "%s", ""); at the end
*/
#define AV_LOG_SKIP_REPEATED 1
/**
* Include the log severity in messages originating from codecs.
*
* Results in messages such as:
* [rawvideo @ 0xDEADBEEF] [error] encode did not produce valid pts
*/
#define AV_LOG_PRINT_LEVEL 2
void av_log_set_flags(int arg);
int av_log_get_flags(void);
/**
* @}
*/
#endif /* AVUTIL_LOG_H */

View File

@ -0,0 +1,80 @@
/*
* 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
* @ingroup lavu
* Utility Preprocessor macros
*/
#ifndef AVUTIL_MACROS_H
#define AVUTIL_MACROS_H
#include "libavutil/avconfig.h"
#if AV_HAVE_BIGENDIAN
# define AV_NE(be, le) (be)
#else
# define AV_NE(be, le) (le)
#endif
/**
* Comparator.
* For two numerical expressions x and y, gives 1 if x > y, -1 if x < y, and 0
* if x == y. This is useful for instance in a qsort comparator callback.
* Furthermore, compilers are able to optimize this to branchless code, and
* there is no risk of overflow with signed types.
* As with many macros, this evaluates its argument multiple times, it thus
* must not have a side-effect.
*/
#define FFDIFFSIGN(x,y) (((x)>(y)) - ((x)<(y)))
#define FFMAX(a,b) ((a) > (b) ? (a) : (b))
#define FFMAX3(a,b,c) FFMAX(FFMAX(a,b),c)
#define FFMIN(a,b) ((a) > (b) ? (b) : (a))
#define FFMIN3(a,b,c) FFMIN(FFMIN(a,b),c)
#define FFSWAP(type,a,b) do{type SWAP_tmp= b; b= a; a= SWAP_tmp;}while(0)
#define FF_ARRAY_ELEMS(a) (sizeof(a) / sizeof((a)[0]))
#define MKTAG(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((unsigned)(d) << 24))
#define MKBETAG(a,b,c,d) ((d) | ((c) << 8) | ((b) << 16) | ((unsigned)(a) << 24))
/**
* @addtogroup preproc_misc Preprocessor String Macros
*
* String manipulation macros
*
* @{
*/
#define AV_STRINGIFY(s) AV_TOSTRING(s)
#define AV_TOSTRING(s) #s
#define AV_GLUE(a, b) a ## b
#define AV_JOIN(a, b) AV_GLUE(a, b)
/**
* @}
*/
#define AV_PRAGMA(s) _Pragma(#s)
#define FFALIGN(x, a) (((x)+(a)-1)&~((a)-1))
#endif /* AVUTIL_MACROS_H */

View File

@ -0,0 +1,319 @@
/*
* Copyright (c) 2005-2012 Michael Niedermayer <michaelni@gmx.at>
*
* 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
* miscellaneous math routines and tables
*/
#include <stdint.h>
#include <limits.h>
#include "avutil.h"
#include "mathematics.h"
#include "libavutil/intmath.h"
#include "libavutil/common.h"
#include "avassert.h"
/* Stein's binary GCD algorithm:
* https://en.wikipedia.org/wiki/Binary_GCD_algorithm */
int64_t av_gcd(int64_t a, int64_t b) {
int za, zb, k;
int64_t u, v;
if (a == 0)
return b;
if (b == 0)
return a;
za = ff_ctzll(a);
zb = ff_ctzll(b);
k = FFMIN(za, zb);
u = llabs(a >> za);
v = llabs(b >> zb);
while (u != v) {
if (u > v)
FFSWAP(int64_t, v, u);
v -= u;
v >>= ff_ctzll(v);
}
return (uint64_t)u << k;
}
int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding rnd)
{
int64_t r = 0;
av_assert2(c > 0);
av_assert2(b >=0);
av_assert2((unsigned)(rnd&~AV_ROUND_PASS_MINMAX)<=5 && (rnd&~AV_ROUND_PASS_MINMAX)!=4);
if (c <= 0 || b < 0 || !((unsigned)(rnd&~AV_ROUND_PASS_MINMAX)<=5 && (rnd&~AV_ROUND_PASS_MINMAX)!=4))
return INT64_MIN;
if (rnd & AV_ROUND_PASS_MINMAX) {
if (a == INT64_MIN || a == INT64_MAX)
return a;
rnd -= AV_ROUND_PASS_MINMAX;
}
if (a < 0)
return -(uint64_t)av_rescale_rnd(-FFMAX(a, -INT64_MAX), b, c, rnd ^ ((rnd >> 1) & 1));
if (rnd == AV_ROUND_NEAR_INF)
r = c / 2;
else if (rnd & 1)
r = c - 1;
if (b <= INT_MAX && c <= INT_MAX) {
if (a <= INT_MAX)
return (a * b + r) / c;
else {
int64_t ad = a / c;
int64_t a2 = (a % c * b + r) / c;
if (ad >= INT32_MAX && b && ad > (INT64_MAX - a2) / b)
return INT64_MIN;
return ad * b + a2;
}
} else {
#if 1
uint64_t a0 = a & 0xFFFFFFFF;
uint64_t a1 = a >> 32;
uint64_t b0 = b & 0xFFFFFFFF;
uint64_t b1 = b >> 32;
uint64_t t1 = a0 * b1 + a1 * b0;
uint64_t t1a = t1 << 32;
int i;
a0 = a0 * b0 + t1a;
a1 = a1 * b1 + (t1 >> 32) + (a0 < t1a);
a0 += r;
a1 += a0 < r;
for (i = 63; i >= 0; i--) {
a1 += a1 + ((a0 >> i) & 1);
t1 += t1;
if (c <= a1) {
a1 -= c;
t1++;
}
}
if (t1 > INT64_MAX)
return INT64_MIN;
return t1;
#else
/* reference code doing (a*b + r) / c, requires libavutil/integer.h */
AVInteger ai;
ai = av_mul_i(av_int2i(a), av_int2i(b));
ai = av_add_i(ai, av_int2i(r));
return av_i2int(av_div_i(ai, av_int2i(c)));
#endif
}
}
int64_t av_rescale(int64_t a, int64_t b, int64_t c)
{
return av_rescale_rnd(a, b, c, AV_ROUND_NEAR_INF);
}
int64_t av_rescale_q_rnd(int64_t a, AVRational bq, AVRational cq,
enum AVRounding rnd)
{
int64_t b = bq.num * (int64_t)cq.den;
int64_t c = cq.num * (int64_t)bq.den;
return av_rescale_rnd(a, b, c, rnd);
}
int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq)
{
return av_rescale_q_rnd(a, bq, cq, AV_ROUND_NEAR_INF);
}
int av_compare_ts(int64_t ts_a, AVRational tb_a, int64_t ts_b, AVRational tb_b)
{
int64_t a = tb_a.num * (int64_t)tb_b.den;
int64_t b = tb_b.num * (int64_t)tb_a.den;
if ((FFABS64U(ts_a)|a|FFABS64U(ts_b)|b) <= INT_MAX)
return (ts_a*a > ts_b*b) - (ts_a*a < ts_b*b);
if (av_rescale_rnd(ts_a, a, b, AV_ROUND_DOWN) < ts_b)
return -1;
if (av_rescale_rnd(ts_b, b, a, AV_ROUND_DOWN) < ts_a)
return 1;
return 0;
}
int64_t av_compare_mod(uint64_t a, uint64_t b, uint64_t mod)
{
int64_t c = (a - b) & (mod - 1);
if (c > (mod >> 1))
c -= mod;
return c;
}
int64_t av_rescale_delta(AVRational in_tb, int64_t in_ts, AVRational fs_tb, int duration, int64_t *last, AVRational out_tb){
int64_t a, b, this;
av_assert0(in_ts != AV_NOPTS_VALUE);
av_assert0(duration >= 0);
if (*last == AV_NOPTS_VALUE || !duration || in_tb.num*(int64_t)out_tb.den <= out_tb.num*(int64_t)in_tb.den) {
simple_round:
*last = av_rescale_q(in_ts, in_tb, fs_tb) + duration;
return av_rescale_q(in_ts, in_tb, out_tb);
}
a = av_rescale_q_rnd(2*in_ts-1, in_tb, fs_tb, AV_ROUND_DOWN) >>1;
b = (av_rescale_q_rnd(2*in_ts+1, in_tb, fs_tb, AV_ROUND_UP )+1)>>1;
if (*last < 2*a - b || *last > 2*b - a)
goto simple_round;
this = av_clip64(*last, a, b);
*last = this + duration;
return av_rescale_q(this, fs_tb, out_tb);
}
int64_t av_add_stable(AVRational ts_tb, int64_t ts, AVRational inc_tb, int64_t inc)
{
int64_t m, d;
if (inc != 1)
inc_tb = av_mul_q(inc_tb, (AVRational) {inc, 1});
m = inc_tb.num * (int64_t)ts_tb.den;
d = inc_tb.den * (int64_t)ts_tb.num;
if (m % d == 0 && ts <= INT64_MAX - m / d)
return ts + m / d;
if (m < d)
return ts;
{
int64_t old = av_rescale_q(ts, ts_tb, inc_tb);
int64_t old_ts = av_rescale_q(old, inc_tb, ts_tb);
if (old == INT64_MAX || old == AV_NOPTS_VALUE || old_ts == AV_NOPTS_VALUE)
return ts;
return av_sat_add64(av_rescale_q(old + 1, inc_tb, ts_tb), ts - old_ts);
}
}
static inline double eval_poly(const double *coeff, int size, double x) {
double sum = coeff[size-1];
int i;
for (i = size-2; i >= 0; --i) {
sum *= x;
sum += coeff[i];
}
return sum;
}
/**
* 0th order modified bessel function of the first kind.
* Algorithm taken from the Boost project, source:
* https://searchcode.com/codesearch/view/14918379/
* Use, modification and distribution are subject to the
* Boost Software License, Version 1.0 (see notice below).
* Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
double av_bessel_i0(double x) {
// Modified Bessel function of the first kind of order zero
// minimax rational approximations on intervals, see
// Blair and Edwards, Chalk River Report AECL-4928, 1974
static const double p1[] = {
-2.2335582639474375249e+15,
-5.5050369673018427753e+14,
-3.2940087627407749166e+13,
-8.4925101247114157499e+11,
-1.1912746104985237192e+10,
-1.0313066708737980747e+08,
-5.9545626019847898221e+05,
-2.4125195876041896775e+03,
-7.0935347449210549190e+00,
-1.5453977791786851041e-02,
-2.5172644670688975051e-05,
-3.0517226450451067446e-08,
-2.6843448573468483278e-11,
-1.5982226675653184646e-14,
-5.2487866627945699800e-18,
};
static const double q1[] = {
-2.2335582639474375245e+15,
7.8858692566751002988e+12,
-1.2207067397808979846e+10,
1.0377081058062166144e+07,
-4.8527560179962773045e+03,
1.0,
};
static const double p2[] = {
-2.2210262233306573296e-04,
1.3067392038106924055e-02,
-4.4700805721174453923e-01,
5.5674518371240761397e+00,
-2.3517945679239481621e+01,
3.1611322818701131207e+01,
-9.6090021968656180000e+00,
};
static const double q2[] = {
-5.5194330231005480228e-04,
3.2547697594819615062e-02,
-1.1151759188741312645e+00,
1.3982595353892851542e+01,
-6.0228002066743340583e+01,
8.5539563258012929600e+01,
-3.1446690275135491500e+01,
1.0,
};
double y, r, factor;
if (x == 0)
return 1.0;
x = fabs(x);
if (x <= 15) {
y = x * x;
return eval_poly(p1, FF_ARRAY_ELEMS(p1), y) / eval_poly(q1, FF_ARRAY_ELEMS(q1), y);
}
else {
y = 1 / x - 1.0 / 15;
r = eval_poly(p2, FF_ARRAY_ELEMS(p2), y) / eval_poly(q2, FF_ARRAY_ELEMS(q2), y);
factor = exp(x) / sqrt(x);
return factor * r;
}
}

View File

@ -0,0 +1,300 @@
/*
* copyright (c) 2005-2012 Michael Niedermayer <michaelni@gmx.at>
*
* 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
* @addtogroup lavu_math
* Mathematical utilities for working with timestamp and time base.
*/
#ifndef AVUTIL_MATHEMATICS_H
#define AVUTIL_MATHEMATICS_H
#include <stdint.h>
#include <math.h>
#include "attributes.h"
#include "rational.h"
#include "intfloat.h"
#ifndef M_E
#define M_E 2.7182818284590452354 /* e */
#endif
#ifndef M_Ef
#define M_Ef 2.7182818284590452354f /* e */
#endif
#ifndef M_LN2
#define M_LN2 0.69314718055994530942 /* log_e 2 */
#endif
#ifndef M_LN2f
#define M_LN2f 0.69314718055994530942f /* log_e 2 */
#endif
#ifndef M_LN10
#define M_LN10 2.30258509299404568402 /* log_e 10 */
#endif
#ifndef M_LN10f
#define M_LN10f 2.30258509299404568402f /* log_e 10 */
#endif
#ifndef M_LOG2_10
#define M_LOG2_10 3.32192809488736234787 /* log_2 10 */
#endif
#ifndef M_LOG2_10f
#define M_LOG2_10f 3.32192809488736234787f /* log_2 10 */
#endif
#ifndef M_PHI
#define M_PHI 1.61803398874989484820 /* phi / golden ratio */
#endif
#ifndef M_PHIf
#define M_PHIf 1.61803398874989484820f /* phi / golden ratio */
#endif
#ifndef M_PI
#define M_PI 3.14159265358979323846 /* pi */
#endif
#ifndef M_PIf
#define M_PIf 3.14159265358979323846f /* pi */
#endif
#ifndef M_PI_2
#define M_PI_2 1.57079632679489661923 /* pi/2 */
#endif
#ifndef M_PI_2f
#define M_PI_2f 1.57079632679489661923f /* pi/2 */
#endif
#ifndef M_PI_4
#define M_PI_4 0.78539816339744830962 /* pi/4 */
#endif
#ifndef M_PI_4f
#define M_PI_4f 0.78539816339744830962f /* pi/4 */
#endif
#ifndef M_1_PI
#define M_1_PI 0.31830988618379067154 /* 1/pi */
#endif
#ifndef M_1_PIf
#define M_1_PIf 0.31830988618379067154f /* 1/pi */
#endif
#ifndef M_2_PI
#define M_2_PI 0.63661977236758134308 /* 2/pi */
#endif
#ifndef M_2_PIf
#define M_2_PIf 0.63661977236758134308f /* 2/pi */
#endif
#ifndef M_2_SQRTPI
#define M_2_SQRTPI 1.12837916709551257390 /* 2/sqrt(pi) */
#endif
#ifndef M_2_SQRTPIf
#define M_2_SQRTPIf 1.12837916709551257390f /* 2/sqrt(pi) */
#endif
#ifndef M_SQRT1_2
#define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */
#endif
#ifndef M_SQRT1_2f
#define M_SQRT1_2f 0.70710678118654752440f /* 1/sqrt(2) */
#endif
#ifndef M_SQRT2
#define M_SQRT2 1.41421356237309504880 /* sqrt(2) */
#endif
#ifndef M_SQRT2f
#define M_SQRT2f 1.41421356237309504880f /* sqrt(2) */
#endif
#ifndef NAN
#define NAN av_int2float(0x7fc00000)
#endif
#ifndef INFINITY
#define INFINITY av_int2float(0x7f800000)
#endif
/**
* @addtogroup lavu_math
*
* @{
*/
/**
* Rounding methods.
*/
enum AVRounding {
AV_ROUND_ZERO = 0, ///< Round toward zero.
AV_ROUND_INF = 1, ///< Round away from zero.
AV_ROUND_DOWN = 2, ///< Round toward -infinity.
AV_ROUND_UP = 3, ///< Round toward +infinity.
AV_ROUND_NEAR_INF = 5, ///< Round to nearest and halfway cases away from zero.
/**
* Flag telling rescaling functions to pass `INT64_MIN`/`MAX` through
* unchanged, avoiding special cases for #AV_NOPTS_VALUE.
*
* Unlike other values of the enumeration AVRounding, this value is a
* bitmask that must be used in conjunction with another value of the
* enumeration through a bitwise OR, in order to set behavior for normal
* cases.
*
* @code{.c}
* av_rescale_rnd(3, 1, 2, AV_ROUND_UP | AV_ROUND_PASS_MINMAX);
* // Rescaling 3:
* // Calculating 3 * 1 / 2
* // 3 / 2 is rounded up to 2
* // => 2
*
* av_rescale_rnd(AV_NOPTS_VALUE, 1, 2, AV_ROUND_UP | AV_ROUND_PASS_MINMAX);
* // Rescaling AV_NOPTS_VALUE:
* // AV_NOPTS_VALUE == INT64_MIN
* // AV_NOPTS_VALUE is passed through
* // => AV_NOPTS_VALUE
* @endcode
*/
AV_ROUND_PASS_MINMAX = 8192,
};
/**
* Compute the greatest common divisor of two integer operands.
*
* @param a Operand
* @param b Operand
* @return GCD of a and b up to sign; if a >= 0 and b >= 0, return value is >= 0;
* if a == 0 and b == 0, returns 0.
*/
int64_t av_const av_gcd(int64_t a, int64_t b);
/**
* Rescale a 64-bit integer with rounding to nearest.
*
* The operation is mathematically equivalent to `a * b / c`, but writing that
* directly can overflow.
*
* This function is equivalent to av_rescale_rnd() with #AV_ROUND_NEAR_INF.
*
* @see av_rescale_rnd(), av_rescale_q(), av_rescale_q_rnd()
*/
int64_t av_rescale(int64_t a, int64_t b, int64_t c) av_const;
/**
* Rescale a 64-bit integer with specified rounding.
*
* The operation is mathematically equivalent to `a * b / c`, but writing that
* directly can overflow, and does not support different rounding methods.
* If the result is not representable then INT64_MIN is returned.
*
* @see av_rescale(), av_rescale_q(), av_rescale_q_rnd()
*/
int64_t av_rescale_rnd(int64_t a, int64_t b, int64_t c, enum AVRounding rnd) av_const;
/**
* Rescale a 64-bit integer by 2 rational numbers.
*
* The operation is mathematically equivalent to `a * bq / cq`.
*
* This function is equivalent to av_rescale_q_rnd() with #AV_ROUND_NEAR_INF.
*
* @see av_rescale(), av_rescale_rnd(), av_rescale_q_rnd()
*/
int64_t av_rescale_q(int64_t a, AVRational bq, AVRational cq) av_const;
/**
* Rescale a 64-bit integer by 2 rational numbers with specified rounding.
*
* The operation is mathematically equivalent to `a * bq / cq`.
*
* @see av_rescale(), av_rescale_rnd(), av_rescale_q()
*/
int64_t av_rescale_q_rnd(int64_t a, AVRational bq, AVRational cq,
enum AVRounding rnd) av_const;
/**
* Compare two timestamps each in its own time base.
*
* @return One of the following values:
* - -1 if `ts_a` is before `ts_b`
* - 1 if `ts_a` is after `ts_b`
* - 0 if they represent the same position
*
* @warning
* The result of the function is undefined if one of the timestamps is outside
* the `int64_t` range when represented in the other's timebase.
*/
int av_compare_ts(int64_t ts_a, AVRational tb_a, int64_t ts_b, AVRational tb_b);
/**
* Compare the remainders of two integer operands divided by a common divisor.
*
* In other words, compare the least significant `log2(mod)` bits of integers
* `a` and `b`.
*
* @code{.c}
* av_compare_mod(0x11, 0x02, 0x10) < 0 // since 0x11 % 0x10 (0x1) < 0x02 % 0x10 (0x2)
* av_compare_mod(0x11, 0x02, 0x20) > 0 // since 0x11 % 0x20 (0x11) > 0x02 % 0x20 (0x02)
* @endcode
*
* @param a Operand
* @param b Operand
* @param mod Divisor; must be a power of 2
* @return
* - a negative value if `a % mod < b % mod`
* - a positive value if `a % mod > b % mod`
* - zero if `a % mod == b % mod`
*/
int64_t av_compare_mod(uint64_t a, uint64_t b, uint64_t mod);
/**
* Rescale a timestamp while preserving known durations.
*
* This function is designed to be called per audio packet to scale the input
* timestamp to a different time base. Compared to a simple av_rescale_q()
* call, this function is robust against possible inconsistent frame durations.
*
* The `last` parameter is a state variable that must be preserved for all
* subsequent calls for the same stream. For the first call, `*last` should be
* initialized to #AV_NOPTS_VALUE.
*
* @param[in] in_tb Input time base
* @param[in] in_ts Input timestamp
* @param[in] fs_tb Duration time base; typically this is finer-grained
* (greater) than `in_tb` and `out_tb`
* @param[in] duration Duration till the next call to this function (i.e.
* duration of the current packet/frame)
* @param[in,out] last Pointer to a timestamp expressed in terms of
* `fs_tb`, acting as a state variable
* @param[in] out_tb Output timebase
* @return Timestamp expressed in terms of `out_tb`
*
* @note In the context of this function, "duration" is in term of samples, not
* seconds.
*/
int64_t av_rescale_delta(AVRational in_tb, int64_t in_ts, AVRational fs_tb, int duration, int64_t *last, AVRational out_tb);
/**
* Add a value to a timestamp.
*
* This function guarantees that when the same value is repeatly added that
* no accumulation of rounding errors occurs.
*
* @param[in] ts Input timestamp
* @param[in] ts_tb Input timestamp time base
* @param[in] inc Value to be added
* @param[in] inc_tb Time base of `inc`
*/
int64_t av_add_stable(AVRational ts_tb, int64_t ts, AVRational inc_tb, int64_t inc);
/**
* 0th order modified bessel function of the first kind.
*/
double av_bessel_i0(double x);
/**
* @}
*/
#endif /* AVUTIL_MATHEMATICS_H */

View File

@ -0,0 +1,89 @@
/*
* copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
*
* 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
* @ingroup lavu_md5
* Public header for MD5 hash function implementation.
*/
#ifndef AVUTIL_MD5_H
#define AVUTIL_MD5_H
#include <stddef.h>
#include <stdint.h>
#include "attributes.h"
/**
* @defgroup lavu_md5 MD5
* @ingroup lavu_hash
* MD5 hash function implementation.
*
* @{
*/
extern const int av_md5_size;
struct AVMD5;
/**
* Allocate an AVMD5 context.
*/
struct AVMD5 *av_md5_alloc(void);
/**
* Initialize MD5 hashing.
*
* @param ctx pointer to the function context (of size av_md5_size)
*/
void av_md5_init(struct AVMD5 *ctx);
/**
* Update hash value.
*
* @param ctx hash function context
* @param src input data to update hash with
* @param len input data length
*/
void av_md5_update(struct AVMD5 *ctx, const uint8_t *src, size_t len);
/**
* Finish hashing and output digest value.
*
* @param ctx hash function context
* @param dst buffer where output digest value is stored
*/
void av_md5_final(struct AVMD5 *ctx, uint8_t *dst);
/**
* Hash an array of data.
*
* @param dst The output buffer to write the digest into
* @param src The data to hash
* @param len The length of the data, in bytes
*/
void av_md5_sum(uint8_t *dst, const uint8_t *src, size_t len);
/**
* @}
*/
#endif /* AVUTIL_MD5_H */

View File

@ -0,0 +1,568 @@
/*
* default memory allocator for libavutil
* Copyright (c) 2002 Fabrice Bellard
*
* 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
* default memory allocator for libavutil
*/
#define _XOPEN_SOURCE 600
#include "config.h"
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdatomic.h>
#include <string.h>
#if HAVE_MALLOC_H
#include <malloc.h>
#endif
#include "attributes.h"
#include "avassert.h"
#include "dynarray.h"
#include "error.h"
#include "internal.h"
#include "intreadwrite.h"
#include "macros.h"
#include "mem.h"
#ifdef MALLOC_PREFIX
#define malloc AV_JOIN(MALLOC_PREFIX, malloc)
#define memalign AV_JOIN(MALLOC_PREFIX, memalign)
#define posix_memalign AV_JOIN(MALLOC_PREFIX, posix_memalign)
#define realloc AV_JOIN(MALLOC_PREFIX, realloc)
#define free AV_JOIN(MALLOC_PREFIX, free)
void *malloc(size_t size);
void *memalign(size_t align, size_t size);
int posix_memalign(void **ptr, size_t align, size_t size);
void *realloc(void *ptr, size_t size);
void free(void *ptr);
#endif /* MALLOC_PREFIX */
#define ALIGN (HAVE_AVX512 ? 64 : (HAVE_AVX ? 32 : 16))
/* NOTE: if you want to override these functions with your own
* implementations (not recommended) you have to link libav* as
* dynamic libraries and remove -Wl,-Bsymbolic from the linker flags.
* Note that this will cost performance. */
static atomic_size_t max_alloc_size = ATOMIC_VAR_INIT(INT_MAX);
void av_max_alloc(size_t max){
atomic_store_explicit(&max_alloc_size, max, memory_order_relaxed);
}
static int size_mult(size_t a, size_t b, size_t *r)
{
size_t t;
#if (!defined(__INTEL_COMPILER) && AV_GCC_VERSION_AT_LEAST(5,1)) || AV_HAS_BUILTIN(__builtin_mul_overflow)
if (__builtin_mul_overflow(a, b, &t))
return AVERROR(EINVAL);
#else
t = a * b;
/* Hack inspired from glibc: don't try the division if nelem and elsize
* are both less than sqrt(SIZE_MAX). */
if ((a | b) >= ((size_t)1 << (sizeof(size_t) * 4)) && a && t / a != b)
return AVERROR(EINVAL);
#endif
*r = t;
return 0;
}
void *av_malloc(size_t size)
{
void *ptr = NULL;
if (size > atomic_load_explicit(&max_alloc_size, memory_order_relaxed))
return NULL;
#if HAVE_POSIX_MEMALIGN
if (size) //OS X on SDK 10.6 has a broken posix_memalign implementation
if (posix_memalign(&ptr, ALIGN, size))
ptr = NULL;
#elif HAVE_ALIGNED_MALLOC
ptr = _aligned_malloc(size, ALIGN);
#elif HAVE_MEMALIGN
#ifndef __DJGPP__
ptr = memalign(ALIGN, size);
#else
ptr = memalign(size, ALIGN);
#endif
/* Why 64?
* Indeed, we should align it:
* on 4 for 386
* on 16 for 486
* on 32 for 586, PPro - K6-III
* on 64 for K7 (maybe for P3 too).
* Because L1 and L2 caches are aligned on those values.
* But I don't want to code such logic here!
*/
/* Why 32?
* For AVX ASM. SSE / NEON needs only 16.
* Why not larger? Because I did not see a difference in benchmarks ...
*/
/* benchmarks with P3
* memalign(64) + 1 3071, 3051, 3032
* memalign(64) + 2 3051, 3032, 3041
* memalign(64) + 4 2911, 2896, 2915
* memalign(64) + 8 2545, 2554, 2550
* memalign(64) + 16 2543, 2572, 2563
* memalign(64) + 32 2546, 2545, 2571
* memalign(64) + 64 2570, 2533, 2558
*
* BTW, malloc seems to do 8-byte alignment by default here.
*/
#else
ptr = malloc(size);
#endif
if(!ptr && !size) {
size = 1;
ptr= av_malloc(1);
}
#if CONFIG_MEMORY_POISONING
if (ptr)
memset(ptr, FF_MEMORY_POISON, size);
#endif
return ptr;
}
void *av_realloc(void *ptr, size_t size)
{
void *ret;
if (size > atomic_load_explicit(&max_alloc_size, memory_order_relaxed))
return NULL;
#if HAVE_ALIGNED_MALLOC
ret = _aligned_realloc(ptr, size + !size, ALIGN);
#else
ret = realloc(ptr, size + !size);
#endif
#if CONFIG_MEMORY_POISONING
if (ret && !ptr)
memset(ret, FF_MEMORY_POISON, size);
#endif
return ret;
}
void *av_realloc_f(void *ptr, size_t nelem, size_t elsize)
{
size_t size;
void *r;
if (size_mult(elsize, nelem, &size)) {
av_free(ptr);
return NULL;
}
r = av_realloc(ptr, size);
if (!r)
av_free(ptr);
return r;
}
int av_reallocp(void *ptr, size_t size)
{
void *val;
if (!size) {
av_freep(ptr);
return 0;
}
memcpy(&val, ptr, sizeof(val));
val = av_realloc(val, size);
if (!val) {
av_freep(ptr);
return AVERROR(ENOMEM);
}
memcpy(ptr, &val, sizeof(val));
return 0;
}
void *av_malloc_array(size_t nmemb, size_t size)
{
size_t result;
if (size_mult(nmemb, size, &result) < 0)
return NULL;
return av_malloc(result);
}
void *av_realloc_array(void *ptr, size_t nmemb, size_t size)
{
size_t result;
if (size_mult(nmemb, size, &result) < 0)
return NULL;
return av_realloc(ptr, result);
}
int av_reallocp_array(void *ptr, size_t nmemb, size_t size)
{
void *val;
memcpy(&val, ptr, sizeof(val));
val = av_realloc_f(val, nmemb, size);
memcpy(ptr, &val, sizeof(val));
if (!val && nmemb && size)
return AVERROR(ENOMEM);
return 0;
}
void av_free(void *ptr)
{
#if HAVE_ALIGNED_MALLOC
_aligned_free(ptr);
#else
free(ptr);
#endif
}
void av_freep(void *arg)
{
void *val;
memcpy(&val, arg, sizeof(val));
memcpy(arg, &(void *){ NULL }, sizeof(val));
av_free(val);
}
void *av_mallocz(size_t size)
{
void *ptr = av_malloc(size);
if (ptr)
memset(ptr, 0, size);
return ptr;
}
void *av_calloc(size_t nmemb, size_t size)
{
size_t result;
if (size_mult(nmemb, size, &result) < 0)
return NULL;
return av_mallocz(result);
}
char *av_strdup(const char *s)
{
char *ptr = NULL;
if (s) {
size_t len = strlen(s) + 1;
ptr = av_realloc(NULL, len);
if (ptr)
memcpy(ptr, s, len);
}
return ptr;
}
char *av_strndup(const char *s, size_t len)
{
char *ret = NULL, *end;
if (!s)
return NULL;
end = memchr(s, 0, len);
if (end)
len = end - s;
ret = av_realloc(NULL, len + 1);
if (!ret)
return NULL;
memcpy(ret, s, len);
ret[len] = 0;
return ret;
}
void *av_memdup(const void *p, size_t size)
{
void *ptr = NULL;
if (p) {
ptr = av_malloc(size);
if (ptr)
memcpy(ptr, p, size);
}
return ptr;
}
int av_dynarray_add_nofree(void *tab_ptr, int *nb_ptr, void *elem)
{
void **tab;
memcpy(&tab, tab_ptr, sizeof(tab));
FF_DYNARRAY_ADD(INT_MAX, sizeof(*tab), tab, *nb_ptr, {
tab[*nb_ptr] = elem;
memcpy(tab_ptr, &tab, sizeof(tab));
}, {
return AVERROR(ENOMEM);
});
return 0;
}
void av_dynarray_add(void *tab_ptr, int *nb_ptr, void *elem)
{
void **tab;
memcpy(&tab, tab_ptr, sizeof(tab));
FF_DYNARRAY_ADD(INT_MAX, sizeof(*tab), tab, *nb_ptr, {
tab[*nb_ptr] = elem;
memcpy(tab_ptr, &tab, sizeof(tab));
}, {
*nb_ptr = 0;
av_freep(tab_ptr);
});
}
void *av_dynarray2_add(void **tab_ptr, int *nb_ptr, size_t elem_size,
const uint8_t *elem_data)
{
uint8_t *tab_elem_data = NULL;
FF_DYNARRAY_ADD(INT_MAX, elem_size, *tab_ptr, *nb_ptr, {
tab_elem_data = (uint8_t *)*tab_ptr + (*nb_ptr) * elem_size;
if (elem_data)
memcpy(tab_elem_data, elem_data, elem_size);
else if (CONFIG_MEMORY_POISONING)
memset(tab_elem_data, FF_MEMORY_POISON, elem_size);
}, {
av_freep(tab_ptr);
*nb_ptr = 0;
});
return tab_elem_data;
}
static void fill16(uint8_t *dst, int len)
{
uint32_t v = AV_RN16(dst - 2);
v |= v << 16;
while (len >= 4) {
AV_WN32(dst, v);
dst += 4;
len -= 4;
}
while (len--) {
*dst = dst[-2];
dst++;
}
}
static void fill24(uint8_t *dst, int len)
{
#if HAVE_BIGENDIAN
uint32_t v = AV_RB24(dst - 3);
uint32_t a = v << 8 | v >> 16;
uint32_t b = v << 16 | v >> 8;
uint32_t c = v << 24 | v;
#else
uint32_t v = AV_RL24(dst - 3);
uint32_t a = v | v << 24;
uint32_t b = v >> 8 | v << 16;
uint32_t c = v >> 16 | v << 8;
#endif
while (len >= 12) {
AV_WN32(dst, a);
AV_WN32(dst + 4, b);
AV_WN32(dst + 8, c);
dst += 12;
len -= 12;
}
if (len >= 4) {
AV_WN32(dst, a);
dst += 4;
len -= 4;
}
if (len >= 4) {
AV_WN32(dst, b);
dst += 4;
len -= 4;
}
while (len--) {
*dst = dst[-3];
dst++;
}
}
static void fill32(uint8_t *dst, int len)
{
uint32_t v = AV_RN32(dst - 4);
#if HAVE_FAST_64BIT
uint64_t v2= v + ((uint64_t)v<<32);
while (len >= 32) {
AV_WN64(dst , v2);
AV_WN64(dst+ 8, v2);
AV_WN64(dst+16, v2);
AV_WN64(dst+24, v2);
dst += 32;
len -= 32;
}
#endif
while (len >= 4) {
AV_WN32(dst, v);
dst += 4;
len -= 4;
}
while (len--) {
*dst = dst[-4];
dst++;
}
}
void av_memcpy_backptr(uint8_t *dst, int back, int cnt)
{
const uint8_t *src = &dst[-back];
if (!back)
return;
if (back == 1) {
memset(dst, *src, cnt);
} else if (back == 2) {
fill16(dst, cnt);
} else if (back == 3) {
fill24(dst, cnt);
} else if (back == 4) {
fill32(dst, cnt);
} else {
if (cnt >= 16) {
int blocklen = back;
while (cnt > blocklen) {
memcpy(dst, src, blocklen);
dst += blocklen;
cnt -= blocklen;
blocklen <<= 1;
}
memcpy(dst, src, cnt);
return;
}
if (cnt >= 8) {
AV_COPY32U(dst, src);
AV_COPY32U(dst + 4, src + 4);
src += 8;
dst += 8;
cnt -= 8;
}
if (cnt >= 4) {
AV_COPY32U(dst, src);
src += 4;
dst += 4;
cnt -= 4;
}
if (cnt >= 2) {
AV_COPY16U(dst, src);
src += 2;
dst += 2;
cnt -= 2;
}
if (cnt)
*dst = *src;
}
}
void *av_fast_realloc(void *ptr, unsigned int *size, size_t min_size)
{
size_t max_size;
if (min_size <= *size)
return ptr;
max_size = atomic_load_explicit(&max_alloc_size, memory_order_relaxed);
/* *size is an unsigned, so the real maximum is <= UINT_MAX. */
max_size = FFMIN(max_size, UINT_MAX);
if (min_size > max_size) {
*size = 0;
return NULL;
}
min_size = FFMIN(max_size, FFMAX(min_size + min_size / 16 + 32, min_size));
ptr = av_realloc(ptr, min_size);
/* we could set this to the unmodified min_size but this is safer
* if the user lost the ptr and uses NULL now
*/
if (!ptr)
min_size = 0;
*size = min_size;
return ptr;
}
static inline void fast_malloc(void *ptr, unsigned int *size, size_t min_size, int zero_realloc)
{
size_t max_size;
void *val;
memcpy(&val, ptr, sizeof(val));
if (min_size <= *size) {
av_assert0(val || !min_size);
return;
}
max_size = atomic_load_explicit(&max_alloc_size, memory_order_relaxed);
/* *size is an unsigned, so the real maximum is <= UINT_MAX. */
max_size = FFMIN(max_size, UINT_MAX);
if (min_size > max_size) {
av_freep(ptr);
*size = 0;
return;
}
min_size = FFMIN(max_size, FFMAX(min_size + min_size / 16 + 32, min_size));
av_freep(ptr);
val = zero_realloc ? av_mallocz(min_size) : av_malloc(min_size);
memcpy(ptr, &val, sizeof(val));
if (!val)
min_size = 0;
*size = min_size;
return;
}
void av_fast_malloc(void *ptr, unsigned int *size, size_t min_size)
{
fast_malloc(ptr, size, min_size, 0);
}
void av_fast_mallocz(void *ptr, unsigned int *size, size_t min_size)
{
fast_malloc(ptr, size, min_size, 1);
}
int av_size_mult(size_t a, size_t b, size_t *r)
{
return size_mult(a, b, r);
}

View File

@ -0,0 +1,609 @@
/*
* copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
*
* 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
* @ingroup lavu_mem
* Memory handling functions
*/
#ifndef AVUTIL_MEM_H
#define AVUTIL_MEM_H
#include <limits.h>
#include <stdint.h>
#include "attributes.h"
#include "avutil.h"
#include "version.h"
/**
* @addtogroup lavu_mem
* Utilities for manipulating memory.
*
* FFmpeg has several applications of memory that are not required of a typical
* program. For example, the computing-heavy components like video decoding and
* encoding can be sped up significantly through the use of aligned memory.
*
* However, for each of FFmpeg's applications of memory, there might not be a
* recognized or standardized API for that specific use. Memory alignment, for
* instance, varies wildly depending on operating systems, architectures, and
* compilers. Hence, this component of @ref libavutil is created to make
* dealing with memory consistently possible on all platforms.
*
* @{
*/
/**
* @defgroup lavu_mem_attrs Function Attributes
* Function attributes applicable to memory handling functions.
*
* These function attributes can help compilers emit more useful warnings, or
* generate better code.
* @{
*/
/**
* @def av_malloc_attrib
* Function attribute denoting a malloc-like function.
*
* @see <a href="https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-g_t_0040code_007bmalloc_007d-function-attribute-3251">Function attribute `malloc` in GCC's documentation</a>
*/
#if AV_GCC_VERSION_AT_LEAST(3,1)
#define av_malloc_attrib __attribute__((__malloc__))
#else
#define av_malloc_attrib
#endif
/**
* @def av_alloc_size(...)
* Function attribute used on a function that allocates memory, whose size is
* given by the specified parameter(s).
*
* @code{.c}
* void *av_malloc(size_t size) av_alloc_size(1);
* void *av_calloc(size_t nmemb, size_t size) av_alloc_size(1, 2);
* @endcode
*
* @param ... One or two parameter indexes, separated by a comma
*
* @see <a href="https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#index-g_t_0040code_007balloc_005fsize_007d-function-attribute-3220">Function attribute `alloc_size` in GCC's documentation</a>
*/
#if AV_GCC_VERSION_AT_LEAST(4,3)
#define av_alloc_size(...) __attribute__((alloc_size(__VA_ARGS__)))
#else
#define av_alloc_size(...)
#endif
/**
* @}
*/
/**
* @defgroup lavu_mem_funcs Heap Management
* Functions responsible for allocating, freeing, and copying memory.
*
* All memory allocation functions have a built-in upper limit of `INT_MAX`
* bytes. This may be changed with av_max_alloc(), although exercise extreme
* caution when doing so.
*
* @{
*/
/**
* Allocate a memory block with alignment suitable for all memory accesses
* (including vectors if available on the CPU).
*
* @param size Size in bytes for the memory block to be allocated
* @return Pointer to the allocated block, or `NULL` if the block cannot
* be allocated
* @see av_mallocz()
*/
void *av_malloc(size_t size) av_malloc_attrib av_alloc_size(1);
/**
* Allocate a memory block with alignment suitable for all memory accesses
* (including vectors if available on the CPU) and zero all the bytes of the
* block.
*
* @param size Size in bytes for the memory block to be allocated
* @return Pointer to the allocated block, or `NULL` if it cannot be allocated
* @see av_malloc()
*/
void *av_mallocz(size_t size) av_malloc_attrib av_alloc_size(1);
/**
* Allocate a memory block for an array with av_malloc().
*
* The allocated memory will have size `size * nmemb` bytes.
*
* @param nmemb Number of element
* @param size Size of a single element
* @return Pointer to the allocated block, or `NULL` if the block cannot
* be allocated
* @see av_malloc()
*/
av_alloc_size(1, 2) void *av_malloc_array(size_t nmemb, size_t size);
/**
* Allocate a memory block for an array with av_mallocz().
*
* The allocated memory will have size `size * nmemb` bytes.
*
* @param nmemb Number of elements
* @param size Size of the single element
* @return Pointer to the allocated block, or `NULL` if the block cannot
* be allocated
*
* @see av_mallocz()
* @see av_malloc_array()
*/
void *av_calloc(size_t nmemb, size_t size) av_malloc_attrib av_alloc_size(1, 2);
/**
* Allocate, reallocate, or free a block of memory.
*
* If `ptr` is `NULL` and `size` > 0, allocate a new block. Otherwise, expand or
* shrink that block of memory according to `size`.
*
* @param ptr Pointer to a memory block already allocated with
* av_realloc() or `NULL`
* @param size Size in bytes of the memory block to be allocated or
* reallocated
*
* @return Pointer to a newly-reallocated block or `NULL` if the block
* cannot be reallocated
*
* @warning Unlike av_malloc(), the returned pointer is not guaranteed to be
* correctly aligned. The returned pointer must be freed after even
* if size is zero.
* @see av_fast_realloc()
* @see av_reallocp()
*/
void *av_realloc(void *ptr, size_t size) av_alloc_size(2);
/**
* Allocate, reallocate, or free a block of memory through a pointer to a
* pointer.
*
* If `*ptr` is `NULL` and `size` > 0, allocate a new block. If `size` is
* zero, free the memory block pointed to by `*ptr`. Otherwise, expand or
* shrink that block of memory according to `size`.
*
* @param[in,out] ptr Pointer to a pointer to a memory block already allocated
* with av_realloc(), or a pointer to `NULL`. The pointer
* is updated on success, or freed on failure.
* @param[in] size Size in bytes for the memory block to be allocated or
* reallocated
*
* @return Zero on success, an AVERROR error code on failure
*
* @warning Unlike av_malloc(), the allocated memory is not guaranteed to be
* correctly aligned.
*/
av_warn_unused_result
int av_reallocp(void *ptr, size_t size);
/**
* Allocate, reallocate, or free a block of memory.
*
* This function does the same thing as av_realloc(), except:
* - It takes two size arguments and allocates `nelem * elsize` bytes,
* after checking the result of the multiplication for integer overflow.
* - It frees the input block in case of failure, thus avoiding the memory
* leak with the classic
* @code{.c}
* buf = realloc(buf);
* if (!buf)
* return -1;
* @endcode
* pattern.
*/
void *av_realloc_f(void *ptr, size_t nelem, size_t elsize);
/**
* Allocate, reallocate, or free an array.
*
* If `ptr` is `NULL` and `nmemb` > 0, allocate a new block.
*
* @param ptr Pointer to a memory block already allocated with
* av_realloc() or `NULL`
* @param nmemb Number of elements in the array
* @param size Size of the single element of the array
*
* @return Pointer to a newly-reallocated block or NULL if the block
* cannot be reallocated
*
* @warning Unlike av_malloc(), the allocated memory is not guaranteed to be
* correctly aligned. The returned pointer must be freed after even if
* nmemb is zero.
* @see av_reallocp_array()
*/
av_alloc_size(2, 3) void *av_realloc_array(void *ptr, size_t nmemb, size_t size);
/**
* Allocate, reallocate an array through a pointer to a pointer.
*
* If `*ptr` is `NULL` and `nmemb` > 0, allocate a new block.
*
* @param[in,out] ptr Pointer to a pointer to a memory block already
* allocated with av_realloc(), or a pointer to `NULL`.
* The pointer is updated on success, or freed on failure.
* @param[in] nmemb Number of elements
* @param[in] size Size of the single element
*
* @return Zero on success, an AVERROR error code on failure
*
* @warning Unlike av_malloc(), the allocated memory is not guaranteed to be
* correctly aligned. *ptr must be freed after even if nmemb is zero.
*/
int av_reallocp_array(void *ptr, size_t nmemb, size_t size);
/**
* Reallocate the given buffer if it is not large enough, otherwise do nothing.
*
* If the given buffer is `NULL`, then a new uninitialized buffer is allocated.
*
* If the given buffer is not large enough, and reallocation fails, `NULL` is
* returned and `*size` is set to 0, but the original buffer is not changed or
* freed.
*
* A typical use pattern follows:
*
* @code{.c}
* uint8_t *buf = ...;
* uint8_t *new_buf = av_fast_realloc(buf, &current_size, size_needed);
* if (!new_buf) {
* // Allocation failed; clean up original buffer
* av_freep(&buf);
* return AVERROR(ENOMEM);
* }
* @endcode
*
* @param[in,out] ptr Already allocated buffer, or `NULL`
* @param[in,out] size Pointer to the size of buffer `ptr`. `*size` is
* updated to the new allocated size, in particular 0
* in case of failure.
* @param[in] min_size Desired minimal size of buffer `ptr`
* @return `ptr` if the buffer is large enough, a pointer to newly reallocated
* buffer if the buffer was not large enough, or `NULL` in case of
* error
* @see av_realloc()
* @see av_fast_malloc()
*/
void *av_fast_realloc(void *ptr, unsigned int *size, size_t min_size);
/**
* Allocate a buffer, reusing the given one if large enough.
*
* Contrary to av_fast_realloc(), the current buffer contents might not be
* preserved and on error the old buffer is freed, thus no special handling to
* avoid memleaks is necessary.
*
* `*ptr` is allowed to be `NULL`, in which case allocation always happens if
* `size_needed` is greater than 0.
*
* @code{.c}
* uint8_t *buf = ...;
* av_fast_malloc(&buf, &current_size, size_needed);
* if (!buf) {
* // Allocation failed; buf already freed
* return AVERROR(ENOMEM);
* }
* @endcode
*
* @param[in,out] ptr Pointer to pointer to an already allocated buffer.
* `*ptr` will be overwritten with pointer to new
* buffer on success or `NULL` on failure
* @param[in,out] size Pointer to the size of buffer `*ptr`. `*size` is
* updated to the new allocated size, in particular 0
* in case of failure.
* @param[in] min_size Desired minimal size of buffer `*ptr`
* @see av_realloc()
* @see av_fast_mallocz()
*/
void av_fast_malloc(void *ptr, unsigned int *size, size_t min_size);
/**
* Allocate and clear a buffer, reusing the given one if large enough.
*
* Like av_fast_malloc(), but all newly allocated space is initially cleared.
* Reused buffer is not cleared.
*
* `*ptr` is allowed to be `NULL`, in which case allocation always happens if
* `size_needed` is greater than 0.
*
* @param[in,out] ptr Pointer to pointer to an already allocated buffer.
* `*ptr` will be overwritten with pointer to new
* buffer on success or `NULL` on failure
* @param[in,out] size Pointer to the size of buffer `*ptr`. `*size` is
* updated to the new allocated size, in particular 0
* in case of failure.
* @param[in] min_size Desired minimal size of buffer `*ptr`
* @see av_fast_malloc()
*/
void av_fast_mallocz(void *ptr, unsigned int *size, size_t min_size);
/**
* Free a memory block which has been allocated with a function of av_malloc()
* or av_realloc() family.
*
* @param ptr Pointer to the memory block which should be freed.
*
* @note `ptr = NULL` is explicitly allowed.
* @note It is recommended that you use av_freep() instead, to prevent leaving
* behind dangling pointers.
* @see av_freep()
*/
void av_free(void *ptr);
/**
* Free a memory block which has been allocated with a function of av_malloc()
* or av_realloc() family, and set the pointer pointing to it to `NULL`.
*
* @code{.c}
* uint8_t *buf = av_malloc(16);
* av_free(buf);
* // buf now contains a dangling pointer to freed memory, and accidental
* // dereference of buf will result in a use-after-free, which may be a
* // security risk.
*
* uint8_t *buf = av_malloc(16);
* av_freep(&buf);
* // buf is now NULL, and accidental dereference will only result in a
* // NULL-pointer dereference.
* @endcode
*
* @param ptr Pointer to the pointer to the memory block which should be freed
* @note `*ptr = NULL` is safe and leads to no action.
* @see av_free()
*/
void av_freep(void *ptr);
/**
* Duplicate a string.
*
* @param s String to be duplicated
* @return Pointer to a newly-allocated string containing a
* copy of `s` or `NULL` if the string cannot be allocated
* @see av_strndup()
*/
char *av_strdup(const char *s) av_malloc_attrib;
/**
* Duplicate a substring of a string.
*
* @param s String to be duplicated
* @param len Maximum length of the resulting string (not counting the
* terminating byte)
* @return Pointer to a newly-allocated string containing a
* substring of `s` or `NULL` if the string cannot be allocated
*/
char *av_strndup(const char *s, size_t len) av_malloc_attrib;
/**
* Duplicate a buffer with av_malloc().
*
* @param p Buffer to be duplicated
* @param size Size in bytes of the buffer copied
* @return Pointer to a newly allocated buffer containing a
* copy of `p` or `NULL` if the buffer cannot be allocated
*/
void *av_memdup(const void *p, size_t size);
/**
* Overlapping memcpy() implementation.
*
* @param dst Destination buffer
* @param back Number of bytes back to start copying (i.e. the initial size of
* the overlapping window); must be > 0
* @param cnt Number of bytes to copy; must be >= 0
*
* @note `cnt > back` is valid, this will copy the bytes we just copied,
* thus creating a repeating pattern with a period length of `back`.
*/
void av_memcpy_backptr(uint8_t *dst, int back, int cnt);
/**
* @}
*/
/**
* @defgroup lavu_mem_dynarray Dynamic Array
*
* Utilities to make an array grow when needed.
*
* Sometimes, the programmer would want to have an array that can grow when
* needed. The libavutil dynamic array utilities fill that need.
*
* libavutil supports two systems of appending elements onto a dynamically
* allocated array, the first one storing the pointer to the value in the
* array, and the second storing the value directly. In both systems, the
* caller is responsible for maintaining a variable containing the length of
* the array, as well as freeing of the array after use.
*
* The first system stores pointers to values in a block of dynamically
* allocated memory. Since only pointers are stored, the function does not need
* to know the size of the type. Both av_dynarray_add() and
* av_dynarray_add_nofree() implement this system.
*
* @code
* type **array = NULL; //< an array of pointers to values
* int nb = 0; //< a variable to keep track of the length of the array
*
* type to_be_added = ...;
* type to_be_added2 = ...;
*
* av_dynarray_add(&array, &nb, &to_be_added);
* if (nb == 0)
* return AVERROR(ENOMEM);
*
* av_dynarray_add(&array, &nb, &to_be_added2);
* if (nb == 0)
* return AVERROR(ENOMEM);
*
* // Now:
* // nb == 2
* // &to_be_added == array[0]
* // &to_be_added2 == array[1]
*
* av_freep(&array);
* @endcode
*
* The second system stores the value directly in a block of memory. As a
* result, the function has to know the size of the type. av_dynarray2_add()
* implements this mechanism.
*
* @code
* type *array = NULL; //< an array of values
* int nb = 0; //< a variable to keep track of the length of the array
*
* type to_be_added = ...;
* type to_be_added2 = ...;
*
* type *addr = av_dynarray2_add((void **)&array, &nb, sizeof(*array), NULL);
* if (!addr)
* return AVERROR(ENOMEM);
* memcpy(addr, &to_be_added, sizeof(to_be_added));
*
* // Shortcut of the above.
* type *addr = av_dynarray2_add((void **)&array, &nb, sizeof(*array),
* (const void *)&to_be_added2);
* if (!addr)
* return AVERROR(ENOMEM);
*
* // Now:
* // nb == 2
* // to_be_added == array[0]
* // to_be_added2 == array[1]
*
* av_freep(&array);
* @endcode
*
* @{
*/
/**
* Add the pointer to an element to a dynamic array.
*
* The array to grow is supposed to be an array of pointers to
* structures, and the element to add must be a pointer to an already
* allocated structure.
*
* The array is reallocated when its size reaches powers of 2.
* Therefore, the amortized cost of adding an element is constant.
*
* In case of success, the pointer to the array is updated in order to
* point to the new grown array, and the number pointed to by `nb_ptr`
* is incremented.
* In case of failure, the array is freed, `*tab_ptr` is set to `NULL` and
* `*nb_ptr` is set to 0.
*
* @param[in,out] tab_ptr Pointer to the array to grow
* @param[in,out] nb_ptr Pointer to the number of elements in the array
* @param[in] elem Element to add
* @see av_dynarray_add_nofree(), av_dynarray2_add()
*/
void av_dynarray_add(void *tab_ptr, int *nb_ptr, void *elem);
/**
* Add an element to a dynamic array.
*
* Function has the same functionality as av_dynarray_add(),
* but it doesn't free memory on fails. It returns error code
* instead and leave current buffer untouched.
*
* @return >=0 on success, negative otherwise
* @see av_dynarray_add(), av_dynarray2_add()
*/
av_warn_unused_result
int av_dynarray_add_nofree(void *tab_ptr, int *nb_ptr, void *elem);
/**
* Add an element of size `elem_size` to a dynamic array.
*
* The array is reallocated when its number of elements reaches powers of 2.
* Therefore, the amortized cost of adding an element is constant.
*
* In case of success, the pointer to the array is updated in order to
* point to the new grown array, and the number pointed to by `nb_ptr`
* is incremented.
* In case of failure, the array is freed, `*tab_ptr` is set to `NULL` and
* `*nb_ptr` is set to 0.
*
* @param[in,out] tab_ptr Pointer to the array to grow
* @param[in,out] nb_ptr Pointer to the number of elements in the array
* @param[in] elem_size Size in bytes of an element in the array
* @param[in] elem_data Pointer to the data of the element to add. If
* `NULL`, the space of the newly added element is
* allocated but left uninitialized.
*
* @return Pointer to the data of the element to copy in the newly allocated
* space
* @see av_dynarray_add(), av_dynarray_add_nofree()
*/
void *av_dynarray2_add(void **tab_ptr, int *nb_ptr, size_t elem_size,
const uint8_t *elem_data);
/**
* @}
*/
/**
* @defgroup lavu_mem_misc Miscellaneous Functions
*
* Other functions related to memory allocation.
*
* @{
*/
/**
* Multiply two `size_t` values checking for overflow.
*
* @param[in] a Operand of multiplication
* @param[in] b Operand of multiplication
* @param[out] r Pointer to the result of the operation
* @return 0 on success, AVERROR(EINVAL) on overflow
*/
int av_size_mult(size_t a, size_t b, size_t *r);
/**
* Set the maximum size that may be allocated in one block.
*
* The value specified with this function is effective for all libavutil's @ref
* lavu_mem_funcs "heap management functions."
*
* By default, the max value is defined as `INT_MAX`.
*
* @param max Value to be set as the new maximum size
*
* @warning Exercise extreme caution when using this function. Don't touch
* this if you do not understand the full consequence of doing so.
*/
void av_max_alloc(size_t max);
/**
* @}
* @}
*/
#endif /* AVUTIL_MEM_H */

View File

@ -0,0 +1,699 @@
/*
* copyright (c) 2006 Michael Niedermayer <michaelni@gmx.at>
*
* 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
*/
#ifndef AVUTIL_PIXFMT_H
#define AVUTIL_PIXFMT_H
/**
* @file
* pixel format definitions
*/
#include "libavutil/avconfig.h"
#include "version.h"
#define AVPALETTE_SIZE 1024
#define AVPALETTE_COUNT 256
/**
* Pixel format.
*
* @note
* AV_PIX_FMT_RGB32 is handled in an endian-specific manner. An RGBA
* color is put together as:
* (A << 24) | (R << 16) | (G << 8) | B
* This is stored as BGRA on little-endian CPU architectures and ARGB on
* big-endian CPUs.
*
* @note
* If the resolution is not a multiple of the chroma subsampling factor
* then the chroma plane resolution must be rounded up.
*
* @par
* When the pixel format is palettized RGB32 (AV_PIX_FMT_PAL8), the palettized
* image data is stored in AVFrame.data[0]. The palette is transported in
* AVFrame.data[1], is 1024 bytes long (256 4-byte entries) and is
* formatted the same as in AV_PIX_FMT_RGB32 described above (i.e., it is
* also endian-specific). Note also that the individual RGB32 palette
* components stored in AVFrame.data[1] should be in the range 0..255.
* This is important as many custom PAL8 video codecs that were designed
* to run on the IBM VGA graphics adapter use 6-bit palette components.
*
* @par
* For all the 8 bits per pixel formats, an RGB32 palette is in data[1] like
* for pal8. This palette is filled in automatically by the function
* allocating the picture.
*/
enum AVPixelFormat {
AV_PIX_FMT_NONE = -1,
AV_PIX_FMT_YUV420P, ///< planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)
AV_PIX_FMT_YUYV422, ///< packed YUV 4:2:2, 16bpp, Y0 Cb Y1 Cr
AV_PIX_FMT_RGB24, ///< packed RGB 8:8:8, 24bpp, RGBRGB...
AV_PIX_FMT_BGR24, ///< packed RGB 8:8:8, 24bpp, BGRBGR...
AV_PIX_FMT_YUV422P, ///< planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples)
AV_PIX_FMT_YUV444P, ///< planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples)
AV_PIX_FMT_YUV410P, ///< planar YUV 4:1:0, 9bpp, (1 Cr & Cb sample per 4x4 Y samples)
AV_PIX_FMT_YUV411P, ///< planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples)
AV_PIX_FMT_GRAY8, ///< Y , 8bpp
AV_PIX_FMT_MONOWHITE, ///< Y , 1bpp, 0 is white, 1 is black, in each byte pixels are ordered from the msb to the lsb
AV_PIX_FMT_MONOBLACK, ///< Y , 1bpp, 0 is black, 1 is white, in each byte pixels are ordered from the msb to the lsb
AV_PIX_FMT_PAL8, ///< 8 bits with AV_PIX_FMT_RGB32 palette
AV_PIX_FMT_YUVJ420P, ///< planar YUV 4:2:0, 12bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV420P and setting color_range
AV_PIX_FMT_YUVJ422P, ///< planar YUV 4:2:2, 16bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV422P and setting color_range
AV_PIX_FMT_YUVJ444P, ///< planar YUV 4:4:4, 24bpp, full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV444P and setting color_range
AV_PIX_FMT_UYVY422, ///< packed YUV 4:2:2, 16bpp, Cb Y0 Cr Y1
AV_PIX_FMT_UYYVYY411, ///< packed YUV 4:1:1, 12bpp, Cb Y0 Y1 Cr Y2 Y3
AV_PIX_FMT_BGR8, ///< packed RGB 3:3:2, 8bpp, (msb)2B 3G 3R(lsb)
AV_PIX_FMT_BGR4, ///< packed RGB 1:2:1 bitstream, 4bpp, (msb)1B 2G 1R(lsb), a byte contains two pixels, the first pixel in the byte is the one composed by the 4 msb bits
AV_PIX_FMT_BGR4_BYTE, ///< packed RGB 1:2:1, 8bpp, (msb)1B 2G 1R(lsb)
AV_PIX_FMT_RGB8, ///< packed RGB 3:3:2, 8bpp, (msb)2R 3G 3B(lsb)
AV_PIX_FMT_RGB4, ///< packed RGB 1:2:1 bitstream, 4bpp, (msb)1R 2G 1B(lsb), a byte contains two pixels, the first pixel in the byte is the one composed by the 4 msb bits
AV_PIX_FMT_RGB4_BYTE, ///< packed RGB 1:2:1, 8bpp, (msb)1R 2G 1B(lsb)
AV_PIX_FMT_NV12, ///< planar YUV 4:2:0, 12bpp, 1 plane for Y and 1 plane for the UV components, which are interleaved (first byte U and the following byte V)
AV_PIX_FMT_NV21, ///< as above, but U and V bytes are swapped
AV_PIX_FMT_ARGB, ///< packed ARGB 8:8:8:8, 32bpp, ARGBARGB...
AV_PIX_FMT_RGBA, ///< packed RGBA 8:8:8:8, 32bpp, RGBARGBA...
AV_PIX_FMT_ABGR, ///< packed ABGR 8:8:8:8, 32bpp, ABGRABGR...
AV_PIX_FMT_BGRA, ///< packed BGRA 8:8:8:8, 32bpp, BGRABGRA...
AV_PIX_FMT_GRAY16BE, ///< Y , 16bpp, big-endian
AV_PIX_FMT_GRAY16LE, ///< Y , 16bpp, little-endian
AV_PIX_FMT_YUV440P, ///< planar YUV 4:4:0 (1 Cr & Cb sample per 1x2 Y samples)
AV_PIX_FMT_YUVJ440P, ///< planar YUV 4:4:0 full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV440P and setting color_range
AV_PIX_FMT_YUVA420P, ///< planar YUV 4:2:0, 20bpp, (1 Cr & Cb sample per 2x2 Y & A samples)
AV_PIX_FMT_RGB48BE, ///< packed RGB 16:16:16, 48bpp, 16R, 16G, 16B, the 2-byte value for each R/G/B component is stored as big-endian
AV_PIX_FMT_RGB48LE, ///< packed RGB 16:16:16, 48bpp, 16R, 16G, 16B, the 2-byte value for each R/G/B component is stored as little-endian
AV_PIX_FMT_RGB565BE, ///< packed RGB 5:6:5, 16bpp, (msb) 5R 6G 5B(lsb), big-endian
AV_PIX_FMT_RGB565LE, ///< packed RGB 5:6:5, 16bpp, (msb) 5R 6G 5B(lsb), little-endian
AV_PIX_FMT_RGB555BE, ///< packed RGB 5:5:5, 16bpp, (msb)1X 5R 5G 5B(lsb), big-endian , X=unused/undefined
AV_PIX_FMT_RGB555LE, ///< packed RGB 5:5:5, 16bpp, (msb)1X 5R 5G 5B(lsb), little-endian, X=unused/undefined
AV_PIX_FMT_BGR565BE, ///< packed BGR 5:6:5, 16bpp, (msb) 5B 6G 5R(lsb), big-endian
AV_PIX_FMT_BGR565LE, ///< packed BGR 5:6:5, 16bpp, (msb) 5B 6G 5R(lsb), little-endian
AV_PIX_FMT_BGR555BE, ///< packed BGR 5:5:5, 16bpp, (msb)1X 5B 5G 5R(lsb), big-endian , X=unused/undefined
AV_PIX_FMT_BGR555LE, ///< packed BGR 5:5:5, 16bpp, (msb)1X 5B 5G 5R(lsb), little-endian, X=unused/undefined
/**
* Hardware acceleration through VA-API, data[3] contains a
* VASurfaceID.
*/
AV_PIX_FMT_VAAPI,
AV_PIX_FMT_YUV420P16LE, ///< planar YUV 4:2:0, 24bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian
AV_PIX_FMT_YUV420P16BE, ///< planar YUV 4:2:0, 24bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian
AV_PIX_FMT_YUV422P16LE, ///< planar YUV 4:2:2, 32bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian
AV_PIX_FMT_YUV422P16BE, ///< planar YUV 4:2:2, 32bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian
AV_PIX_FMT_YUV444P16LE, ///< planar YUV 4:4:4, 48bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian
AV_PIX_FMT_YUV444P16BE, ///< planar YUV 4:4:4, 48bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian
AV_PIX_FMT_DXVA2_VLD, ///< HW decoding through DXVA2, Picture.data[3] contains a LPDIRECT3DSURFACE9 pointer
AV_PIX_FMT_RGB444LE, ///< packed RGB 4:4:4, 16bpp, (msb)4X 4R 4G 4B(lsb), little-endian, X=unused/undefined
AV_PIX_FMT_RGB444BE, ///< packed RGB 4:4:4, 16bpp, (msb)4X 4R 4G 4B(lsb), big-endian, X=unused/undefined
AV_PIX_FMT_BGR444LE, ///< packed BGR 4:4:4, 16bpp, (msb)4X 4B 4G 4R(lsb), little-endian, X=unused/undefined
AV_PIX_FMT_BGR444BE, ///< packed BGR 4:4:4, 16bpp, (msb)4X 4B 4G 4R(lsb), big-endian, X=unused/undefined
AV_PIX_FMT_YA8, ///< 8 bits gray, 8 bits alpha
AV_PIX_FMT_Y400A = AV_PIX_FMT_YA8, ///< alias for AV_PIX_FMT_YA8
AV_PIX_FMT_GRAY8A= AV_PIX_FMT_YA8, ///< alias for AV_PIX_FMT_YA8
AV_PIX_FMT_BGR48BE, ///< packed RGB 16:16:16, 48bpp, 16B, 16G, 16R, the 2-byte value for each R/G/B component is stored as big-endian
AV_PIX_FMT_BGR48LE, ///< packed RGB 16:16:16, 48bpp, 16B, 16G, 16R, the 2-byte value for each R/G/B component is stored as little-endian
/**
* The following 12 formats have the disadvantage of needing 1 format for each bit depth.
* Notice that each 9/10 bits sample is stored in 16 bits with extra padding.
* If you want to support multiple bit depths, then using AV_PIX_FMT_YUV420P16* with the bpp stored separately is better.
*/
AV_PIX_FMT_YUV420P9BE, ///< planar YUV 4:2:0, 13.5bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian
AV_PIX_FMT_YUV420P9LE, ///< planar YUV 4:2:0, 13.5bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian
AV_PIX_FMT_YUV420P10BE,///< planar YUV 4:2:0, 15bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian
AV_PIX_FMT_YUV420P10LE,///< planar YUV 4:2:0, 15bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian
AV_PIX_FMT_YUV422P10BE,///< planar YUV 4:2:2, 20bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian
AV_PIX_FMT_YUV422P10LE,///< planar YUV 4:2:2, 20bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian
AV_PIX_FMT_YUV444P9BE, ///< planar YUV 4:4:4, 27bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian
AV_PIX_FMT_YUV444P9LE, ///< planar YUV 4:4:4, 27bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian
AV_PIX_FMT_YUV444P10BE,///< planar YUV 4:4:4, 30bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian
AV_PIX_FMT_YUV444P10LE,///< planar YUV 4:4:4, 30bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian
AV_PIX_FMT_YUV422P9BE, ///< planar YUV 4:2:2, 18bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian
AV_PIX_FMT_YUV422P9LE, ///< planar YUV 4:2:2, 18bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian
AV_PIX_FMT_GBRP, ///< planar GBR 4:4:4 24bpp
AV_PIX_FMT_GBR24P = AV_PIX_FMT_GBRP, // alias for #AV_PIX_FMT_GBRP
AV_PIX_FMT_GBRP9BE, ///< planar GBR 4:4:4 27bpp, big-endian
AV_PIX_FMT_GBRP9LE, ///< planar GBR 4:4:4 27bpp, little-endian
AV_PIX_FMT_GBRP10BE, ///< planar GBR 4:4:4 30bpp, big-endian
AV_PIX_FMT_GBRP10LE, ///< planar GBR 4:4:4 30bpp, little-endian
AV_PIX_FMT_GBRP16BE, ///< planar GBR 4:4:4 48bpp, big-endian
AV_PIX_FMT_GBRP16LE, ///< planar GBR 4:4:4 48bpp, little-endian
AV_PIX_FMT_YUVA422P, ///< planar YUV 4:2:2 24bpp, (1 Cr & Cb sample per 2x1 Y & A samples)
AV_PIX_FMT_YUVA444P, ///< planar YUV 4:4:4 32bpp, (1 Cr & Cb sample per 1x1 Y & A samples)
AV_PIX_FMT_YUVA420P9BE, ///< planar YUV 4:2:0 22.5bpp, (1 Cr & Cb sample per 2x2 Y & A samples), big-endian
AV_PIX_FMT_YUVA420P9LE, ///< planar YUV 4:2:0 22.5bpp, (1 Cr & Cb sample per 2x2 Y & A samples), little-endian
AV_PIX_FMT_YUVA422P9BE, ///< planar YUV 4:2:2 27bpp, (1 Cr & Cb sample per 2x1 Y & A samples), big-endian
AV_PIX_FMT_YUVA422P9LE, ///< planar YUV 4:2:2 27bpp, (1 Cr & Cb sample per 2x1 Y & A samples), little-endian
AV_PIX_FMT_YUVA444P9BE, ///< planar YUV 4:4:4 36bpp, (1 Cr & Cb sample per 1x1 Y & A samples), big-endian
AV_PIX_FMT_YUVA444P9LE, ///< planar YUV 4:4:4 36bpp, (1 Cr & Cb sample per 1x1 Y & A samples), little-endian
AV_PIX_FMT_YUVA420P10BE, ///< planar YUV 4:2:0 25bpp, (1 Cr & Cb sample per 2x2 Y & A samples, big-endian)
AV_PIX_FMT_YUVA420P10LE, ///< planar YUV 4:2:0 25bpp, (1 Cr & Cb sample per 2x2 Y & A samples, little-endian)
AV_PIX_FMT_YUVA422P10BE, ///< planar YUV 4:2:2 30bpp, (1 Cr & Cb sample per 2x1 Y & A samples, big-endian)
AV_PIX_FMT_YUVA422P10LE, ///< planar YUV 4:2:2 30bpp, (1 Cr & Cb sample per 2x1 Y & A samples, little-endian)
AV_PIX_FMT_YUVA444P10BE, ///< planar YUV 4:4:4 40bpp, (1 Cr & Cb sample per 1x1 Y & A samples, big-endian)
AV_PIX_FMT_YUVA444P10LE, ///< planar YUV 4:4:4 40bpp, (1 Cr & Cb sample per 1x1 Y & A samples, little-endian)
AV_PIX_FMT_YUVA420P16BE, ///< planar YUV 4:2:0 40bpp, (1 Cr & Cb sample per 2x2 Y & A samples, big-endian)
AV_PIX_FMT_YUVA420P16LE, ///< planar YUV 4:2:0 40bpp, (1 Cr & Cb sample per 2x2 Y & A samples, little-endian)
AV_PIX_FMT_YUVA422P16BE, ///< planar YUV 4:2:2 48bpp, (1 Cr & Cb sample per 2x1 Y & A samples, big-endian)
AV_PIX_FMT_YUVA422P16LE, ///< planar YUV 4:2:2 48bpp, (1 Cr & Cb sample per 2x1 Y & A samples, little-endian)
AV_PIX_FMT_YUVA444P16BE, ///< planar YUV 4:4:4 64bpp, (1 Cr & Cb sample per 1x1 Y & A samples, big-endian)
AV_PIX_FMT_YUVA444P16LE, ///< planar YUV 4:4:4 64bpp, (1 Cr & Cb sample per 1x1 Y & A samples, little-endian)
AV_PIX_FMT_VDPAU, ///< HW acceleration through VDPAU, Picture.data[3] contains a VdpVideoSurface
AV_PIX_FMT_XYZ12LE, ///< packed XYZ 4:4:4, 36 bpp, (msb) 12X, 12Y, 12Z (lsb), the 2-byte value for each X/Y/Z is stored as little-endian, the 4 lower bits are set to 0
AV_PIX_FMT_XYZ12BE, ///< packed XYZ 4:4:4, 36 bpp, (msb) 12X, 12Y, 12Z (lsb), the 2-byte value for each X/Y/Z is stored as big-endian, the 4 lower bits are set to 0
AV_PIX_FMT_NV16, ///< interleaved chroma YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples)
AV_PIX_FMT_NV20LE, ///< interleaved chroma YUV 4:2:2, 20bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian
AV_PIX_FMT_NV20BE, ///< interleaved chroma YUV 4:2:2, 20bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian
AV_PIX_FMT_RGBA64BE, ///< packed RGBA 16:16:16:16, 64bpp, 16R, 16G, 16B, 16A, the 2-byte value for each R/G/B/A component is stored as big-endian
AV_PIX_FMT_RGBA64LE, ///< packed RGBA 16:16:16:16, 64bpp, 16R, 16G, 16B, 16A, the 2-byte value for each R/G/B/A component is stored as little-endian
AV_PIX_FMT_BGRA64BE, ///< packed RGBA 16:16:16:16, 64bpp, 16B, 16G, 16R, 16A, the 2-byte value for each R/G/B/A component is stored as big-endian
AV_PIX_FMT_BGRA64LE, ///< packed RGBA 16:16:16:16, 64bpp, 16B, 16G, 16R, 16A, the 2-byte value for each R/G/B/A component is stored as little-endian
AV_PIX_FMT_YVYU422, ///< packed YUV 4:2:2, 16bpp, Y0 Cr Y1 Cb
AV_PIX_FMT_YA16BE, ///< 16 bits gray, 16 bits alpha (big-endian)
AV_PIX_FMT_YA16LE, ///< 16 bits gray, 16 bits alpha (little-endian)
AV_PIX_FMT_GBRAP, ///< planar GBRA 4:4:4:4 32bpp
AV_PIX_FMT_GBRAP16BE, ///< planar GBRA 4:4:4:4 64bpp, big-endian
AV_PIX_FMT_GBRAP16LE, ///< planar GBRA 4:4:4:4 64bpp, little-endian
/**
* HW acceleration through QSV, data[3] contains a pointer to the
* mfxFrameSurface1 structure.
*
* Before FFmpeg 5.0:
* mfxFrameSurface1.Data.MemId contains a pointer when importing
* the following frames as QSV frames:
*
* VAAPI:
* mfxFrameSurface1.Data.MemId contains a pointer to VASurfaceID
*
* DXVA2:
* mfxFrameSurface1.Data.MemId contains a pointer to IDirect3DSurface9
*
* FFmpeg 5.0 and above:
* mfxFrameSurface1.Data.MemId contains a pointer to the mfxHDLPair
* structure when importing the following frames as QSV frames:
*
* VAAPI:
* mfxHDLPair.first contains a VASurfaceID pointer.
* mfxHDLPair.second is always MFX_INFINITE.
*
* DXVA2:
* mfxHDLPair.first contains IDirect3DSurface9 pointer.
* mfxHDLPair.second is always MFX_INFINITE.
*
* D3D11:
* mfxHDLPair.first contains a ID3D11Texture2D pointer.
* mfxHDLPair.second contains the texture array index of the frame if the
* ID3D11Texture2D is an array texture, or always MFX_INFINITE if it is a
* normal texture.
*/
AV_PIX_FMT_QSV,
/**
* HW acceleration though MMAL, data[3] contains a pointer to the
* MMAL_BUFFER_HEADER_T structure.
*/
AV_PIX_FMT_MMAL,
AV_PIX_FMT_D3D11VA_VLD, ///< HW decoding through Direct3D11 via old API, Picture.data[3] contains a ID3D11VideoDecoderOutputView pointer
/**
* HW acceleration through CUDA. data[i] contain CUdeviceptr pointers
* exactly as for system memory frames.
*/
AV_PIX_FMT_CUDA,
AV_PIX_FMT_0RGB, ///< packed RGB 8:8:8, 32bpp, XRGBXRGB... X=unused/undefined
AV_PIX_FMT_RGB0, ///< packed RGB 8:8:8, 32bpp, RGBXRGBX... X=unused/undefined
AV_PIX_FMT_0BGR, ///< packed BGR 8:8:8, 32bpp, XBGRXBGR... X=unused/undefined
AV_PIX_FMT_BGR0, ///< packed BGR 8:8:8, 32bpp, BGRXBGRX... X=unused/undefined
AV_PIX_FMT_YUV420P12BE, ///< planar YUV 4:2:0,18bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian
AV_PIX_FMT_YUV420P12LE, ///< planar YUV 4:2:0,18bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian
AV_PIX_FMT_YUV420P14BE, ///< planar YUV 4:2:0,21bpp, (1 Cr & Cb sample per 2x2 Y samples), big-endian
AV_PIX_FMT_YUV420P14LE, ///< planar YUV 4:2:0,21bpp, (1 Cr & Cb sample per 2x2 Y samples), little-endian
AV_PIX_FMT_YUV422P12BE, ///< planar YUV 4:2:2,24bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian
AV_PIX_FMT_YUV422P12LE, ///< planar YUV 4:2:2,24bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian
AV_PIX_FMT_YUV422P14BE, ///< planar YUV 4:2:2,28bpp, (1 Cr & Cb sample per 2x1 Y samples), big-endian
AV_PIX_FMT_YUV422P14LE, ///< planar YUV 4:2:2,28bpp, (1 Cr & Cb sample per 2x1 Y samples), little-endian
AV_PIX_FMT_YUV444P12BE, ///< planar YUV 4:4:4,36bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian
AV_PIX_FMT_YUV444P12LE, ///< planar YUV 4:4:4,36bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian
AV_PIX_FMT_YUV444P14BE, ///< planar YUV 4:4:4,42bpp, (1 Cr & Cb sample per 1x1 Y samples), big-endian
AV_PIX_FMT_YUV444P14LE, ///< planar YUV 4:4:4,42bpp, (1 Cr & Cb sample per 1x1 Y samples), little-endian
AV_PIX_FMT_GBRP12BE, ///< planar GBR 4:4:4 36bpp, big-endian
AV_PIX_FMT_GBRP12LE, ///< planar GBR 4:4:4 36bpp, little-endian
AV_PIX_FMT_GBRP14BE, ///< planar GBR 4:4:4 42bpp, big-endian
AV_PIX_FMT_GBRP14LE, ///< planar GBR 4:4:4 42bpp, little-endian
AV_PIX_FMT_YUVJ411P, ///< planar YUV 4:1:1, 12bpp, (1 Cr & Cb sample per 4x1 Y samples) full scale (JPEG), deprecated in favor of AV_PIX_FMT_YUV411P and setting color_range
AV_PIX_FMT_BAYER_BGGR8, ///< bayer, BGBG..(odd line), GRGR..(even line), 8-bit samples
AV_PIX_FMT_BAYER_RGGB8, ///< bayer, RGRG..(odd line), GBGB..(even line), 8-bit samples
AV_PIX_FMT_BAYER_GBRG8, ///< bayer, GBGB..(odd line), RGRG..(even line), 8-bit samples
AV_PIX_FMT_BAYER_GRBG8, ///< bayer, GRGR..(odd line), BGBG..(even line), 8-bit samples
AV_PIX_FMT_BAYER_BGGR16LE, ///< bayer, BGBG..(odd line), GRGR..(even line), 16-bit samples, little-endian
AV_PIX_FMT_BAYER_BGGR16BE, ///< bayer, BGBG..(odd line), GRGR..(even line), 16-bit samples, big-endian
AV_PIX_FMT_BAYER_RGGB16LE, ///< bayer, RGRG..(odd line), GBGB..(even line), 16-bit samples, little-endian
AV_PIX_FMT_BAYER_RGGB16BE, ///< bayer, RGRG..(odd line), GBGB..(even line), 16-bit samples, big-endian
AV_PIX_FMT_BAYER_GBRG16LE, ///< bayer, GBGB..(odd line), RGRG..(even line), 16-bit samples, little-endian
AV_PIX_FMT_BAYER_GBRG16BE, ///< bayer, GBGB..(odd line), RGRG..(even line), 16-bit samples, big-endian
AV_PIX_FMT_BAYER_GRBG16LE, ///< bayer, GRGR..(odd line), BGBG..(even line), 16-bit samples, little-endian
AV_PIX_FMT_BAYER_GRBG16BE, ///< bayer, GRGR..(odd line), BGBG..(even line), 16-bit samples, big-endian
#if FF_API_XVMC
AV_PIX_FMT_XVMC,///< XVideo Motion Acceleration via common packet passing
#endif
AV_PIX_FMT_YUV440P10LE, ///< planar YUV 4:4:0,20bpp, (1 Cr & Cb sample per 1x2 Y samples), little-endian
AV_PIX_FMT_YUV440P10BE, ///< planar YUV 4:4:0,20bpp, (1 Cr & Cb sample per 1x2 Y samples), big-endian
AV_PIX_FMT_YUV440P12LE, ///< planar YUV 4:4:0,24bpp, (1 Cr & Cb sample per 1x2 Y samples), little-endian
AV_PIX_FMT_YUV440P12BE, ///< planar YUV 4:4:0,24bpp, (1 Cr & Cb sample per 1x2 Y samples), big-endian
AV_PIX_FMT_AYUV64LE, ///< packed AYUV 4:4:4,64bpp (1 Cr & Cb sample per 1x1 Y & A samples), little-endian
AV_PIX_FMT_AYUV64BE, ///< packed AYUV 4:4:4,64bpp (1 Cr & Cb sample per 1x1 Y & A samples), big-endian
AV_PIX_FMT_VIDEOTOOLBOX, ///< hardware decoding through Videotoolbox
AV_PIX_FMT_P010LE, ///< like NV12, with 10bpp per component, data in the high bits, zeros in the low bits, little-endian
AV_PIX_FMT_P010BE, ///< like NV12, with 10bpp per component, data in the high bits, zeros in the low bits, big-endian
AV_PIX_FMT_GBRAP12BE, ///< planar GBR 4:4:4:4 48bpp, big-endian
AV_PIX_FMT_GBRAP12LE, ///< planar GBR 4:4:4:4 48bpp, little-endian
AV_PIX_FMT_GBRAP10BE, ///< planar GBR 4:4:4:4 40bpp, big-endian
AV_PIX_FMT_GBRAP10LE, ///< planar GBR 4:4:4:4 40bpp, little-endian
AV_PIX_FMT_MEDIACODEC, ///< hardware decoding through MediaCodec
AV_PIX_FMT_GRAY12BE, ///< Y , 12bpp, big-endian
AV_PIX_FMT_GRAY12LE, ///< Y , 12bpp, little-endian
AV_PIX_FMT_GRAY10BE, ///< Y , 10bpp, big-endian
AV_PIX_FMT_GRAY10LE, ///< Y , 10bpp, little-endian
AV_PIX_FMT_P016LE, ///< like NV12, with 16bpp per component, little-endian
AV_PIX_FMT_P016BE, ///< like NV12, with 16bpp per component, big-endian
/**
* Hardware surfaces for Direct3D11.
*
* This is preferred over the legacy AV_PIX_FMT_D3D11VA_VLD. The new D3D11
* hwaccel API and filtering support AV_PIX_FMT_D3D11 only.
*
* data[0] contains a ID3D11Texture2D pointer, and data[1] contains the
* texture array index of the frame as intptr_t if the ID3D11Texture2D is
* an array texture (or always 0 if it's a normal texture).
*/
AV_PIX_FMT_D3D11,
AV_PIX_FMT_GRAY9BE, ///< Y , 9bpp, big-endian
AV_PIX_FMT_GRAY9LE, ///< Y , 9bpp, little-endian
AV_PIX_FMT_GBRPF32BE, ///< IEEE-754 single precision planar GBR 4:4:4, 96bpp, big-endian
AV_PIX_FMT_GBRPF32LE, ///< IEEE-754 single precision planar GBR 4:4:4, 96bpp, little-endian
AV_PIX_FMT_GBRAPF32BE, ///< IEEE-754 single precision planar GBRA 4:4:4:4, 128bpp, big-endian
AV_PIX_FMT_GBRAPF32LE, ///< IEEE-754 single precision planar GBRA 4:4:4:4, 128bpp, little-endian
/**
* DRM-managed buffers exposed through PRIME buffer sharing.
*
* data[0] points to an AVDRMFrameDescriptor.
*/
AV_PIX_FMT_DRM_PRIME,
/**
* Hardware surfaces for OpenCL.
*
* data[i] contain 2D image objects (typed in C as cl_mem, used
* in OpenCL as image2d_t) for each plane of the surface.
*/
AV_PIX_FMT_OPENCL,
AV_PIX_FMT_GRAY14BE, ///< Y , 14bpp, big-endian
AV_PIX_FMT_GRAY14LE, ///< Y , 14bpp, little-endian
AV_PIX_FMT_GRAYF32BE, ///< IEEE-754 single precision Y, 32bpp, big-endian
AV_PIX_FMT_GRAYF32LE, ///< IEEE-754 single precision Y, 32bpp, little-endian
AV_PIX_FMT_YUVA422P12BE, ///< planar YUV 4:2:2,24bpp, (1 Cr & Cb sample per 2x1 Y samples), 12b alpha, big-endian
AV_PIX_FMT_YUVA422P12LE, ///< planar YUV 4:2:2,24bpp, (1 Cr & Cb sample per 2x1 Y samples), 12b alpha, little-endian
AV_PIX_FMT_YUVA444P12BE, ///< planar YUV 4:4:4,36bpp, (1 Cr & Cb sample per 1x1 Y samples), 12b alpha, big-endian
AV_PIX_FMT_YUVA444P12LE, ///< planar YUV 4:4:4,36bpp, (1 Cr & Cb sample per 1x1 Y samples), 12b alpha, little-endian
AV_PIX_FMT_NV24, ///< planar YUV 4:4:4, 24bpp, 1 plane for Y and 1 plane for the UV components, which are interleaved (first byte U and the following byte V)
AV_PIX_FMT_NV42, ///< as above, but U and V bytes are swapped
/**
* Vulkan hardware images.
*
* data[0] points to an AVVkFrame
*/
AV_PIX_FMT_VULKAN,
AV_PIX_FMT_Y210BE, ///< packed YUV 4:2:2 like YUYV422, 20bpp, data in the high bits, big-endian
AV_PIX_FMT_Y210LE, ///< packed YUV 4:2:2 like YUYV422, 20bpp, data in the high bits, little-endian
AV_PIX_FMT_X2RGB10LE, ///< packed RGB 10:10:10, 30bpp, (msb)2X 10R 10G 10B(lsb), little-endian, X=unused/undefined
AV_PIX_FMT_X2RGB10BE, ///< packed RGB 10:10:10, 30bpp, (msb)2X 10R 10G 10B(lsb), big-endian, X=unused/undefined
AV_PIX_FMT_X2BGR10LE, ///< packed BGR 10:10:10, 30bpp, (msb)2X 10B 10G 10R(lsb), little-endian, X=unused/undefined
AV_PIX_FMT_X2BGR10BE, ///< packed BGR 10:10:10, 30bpp, (msb)2X 10B 10G 10R(lsb), big-endian, X=unused/undefined
AV_PIX_FMT_P210BE, ///< interleaved chroma YUV 4:2:2, 20bpp, data in the high bits, big-endian
AV_PIX_FMT_P210LE, ///< interleaved chroma YUV 4:2:2, 20bpp, data in the high bits, little-endian
AV_PIX_FMT_P410BE, ///< interleaved chroma YUV 4:4:4, 30bpp, data in the high bits, big-endian
AV_PIX_FMT_P410LE, ///< interleaved chroma YUV 4:4:4, 30bpp, data in the high bits, little-endian
AV_PIX_FMT_P216BE, ///< interleaved chroma YUV 4:2:2, 32bpp, big-endian
AV_PIX_FMT_P216LE, ///< interleaved chroma YUV 4:2:2, 32bpp, little-endian
AV_PIX_FMT_P416BE, ///< interleaved chroma YUV 4:4:4, 48bpp, big-endian
AV_PIX_FMT_P416LE, ///< interleaved chroma YUV 4:4:4, 48bpp, little-endian
AV_PIX_FMT_VUYA, ///< packed VUYA 4:4:4, 32bpp, VUYAVUYA...
AV_PIX_FMT_RGBAF16BE, ///< IEEE-754 half precision packed RGBA 16:16:16:16, 64bpp, RGBARGBA..., big-endian
AV_PIX_FMT_RGBAF16LE, ///< IEEE-754 half precision packed RGBA 16:16:16:16, 64bpp, RGBARGBA..., little-endian
AV_PIX_FMT_VUYX, ///< packed VUYX 4:4:4, 32bpp, Variant of VUYA where alpha channel is left undefined
AV_PIX_FMT_P012LE, ///< like NV12, with 12bpp per component, data in the high bits, zeros in the low bits, little-endian
AV_PIX_FMT_P012BE, ///< like NV12, with 12bpp per component, data in the high bits, zeros in the low bits, big-endian
AV_PIX_FMT_Y212BE, ///< packed YUV 4:2:2 like YUYV422, 24bpp, data in the high bits, zeros in the low bits, big-endian
AV_PIX_FMT_Y212LE, ///< packed YUV 4:2:2 like YUYV422, 24bpp, data in the high bits, zeros in the low bits, little-endian
AV_PIX_FMT_XV30BE, ///< packed XVYU 4:4:4, 32bpp, (msb)2X 10V 10Y 10U(lsb), big-endian, variant of Y410 where alpha channel is left undefined
AV_PIX_FMT_XV30LE, ///< packed XVYU 4:4:4, 32bpp, (msb)2X 10V 10Y 10U(lsb), little-endian, variant of Y410 where alpha channel is left undefined
AV_PIX_FMT_XV36BE, ///< packed XVYU 4:4:4, 48bpp, data in the high bits, zeros in the low bits, big-endian, variant of Y412 where alpha channel is left undefined
AV_PIX_FMT_XV36LE, ///< packed XVYU 4:4:4, 48bpp, data in the high bits, zeros in the low bits, little-endian, variant of Y412 where alpha channel is left undefined
AV_PIX_FMT_RGBF32BE, ///< IEEE-754 single precision packed RGB 32:32:32, 96bpp, RGBRGB..., big-endian
AV_PIX_FMT_RGBF32LE, ///< IEEE-754 single precision packed RGB 32:32:32, 96bpp, RGBRGB..., little-endian
AV_PIX_FMT_RGBAF32BE, ///< IEEE-754 single precision packed RGBA 32:32:32:32, 128bpp, RGBARGBA..., big-endian
AV_PIX_FMT_RGBAF32LE, ///< IEEE-754 single precision packed RGBA 32:32:32:32, 128bpp, RGBARGBA..., little-endian
AV_PIX_FMT_P212BE, ///< interleaved chroma YUV 4:2:2, 24bpp, data in the high bits, big-endian
AV_PIX_FMT_P212LE, ///< interleaved chroma YUV 4:2:2, 24bpp, data in the high bits, little-endian
AV_PIX_FMT_P412BE, ///< interleaved chroma YUV 4:4:4, 36bpp, data in the high bits, big-endian
AV_PIX_FMT_P412LE, ///< interleaved chroma YUV 4:4:4, 36bpp, data in the high bits, little-endian
AV_PIX_FMT_NB ///< number of pixel formats, DO NOT USE THIS if you want to link with shared libav* because the number of formats might differ between versions
};
#if AV_HAVE_BIGENDIAN
# define AV_PIX_FMT_NE(be, le) AV_PIX_FMT_##be
#else
# define AV_PIX_FMT_NE(be, le) AV_PIX_FMT_##le
#endif
#define AV_PIX_FMT_RGB32 AV_PIX_FMT_NE(ARGB, BGRA)
#define AV_PIX_FMT_RGB32_1 AV_PIX_FMT_NE(RGBA, ABGR)
#define AV_PIX_FMT_BGR32 AV_PIX_FMT_NE(ABGR, RGBA)
#define AV_PIX_FMT_BGR32_1 AV_PIX_FMT_NE(BGRA, ARGB)
#define AV_PIX_FMT_0RGB32 AV_PIX_FMT_NE(0RGB, BGR0)
#define AV_PIX_FMT_0BGR32 AV_PIX_FMT_NE(0BGR, RGB0)
#define AV_PIX_FMT_GRAY9 AV_PIX_FMT_NE(GRAY9BE, GRAY9LE)
#define AV_PIX_FMT_GRAY10 AV_PIX_FMT_NE(GRAY10BE, GRAY10LE)
#define AV_PIX_FMT_GRAY12 AV_PIX_FMT_NE(GRAY12BE, GRAY12LE)
#define AV_PIX_FMT_GRAY14 AV_PIX_FMT_NE(GRAY14BE, GRAY14LE)
#define AV_PIX_FMT_GRAY16 AV_PIX_FMT_NE(GRAY16BE, GRAY16LE)
#define AV_PIX_FMT_YA16 AV_PIX_FMT_NE(YA16BE, YA16LE)
#define AV_PIX_FMT_RGB48 AV_PIX_FMT_NE(RGB48BE, RGB48LE)
#define AV_PIX_FMT_RGB565 AV_PIX_FMT_NE(RGB565BE, RGB565LE)
#define AV_PIX_FMT_RGB555 AV_PIX_FMT_NE(RGB555BE, RGB555LE)
#define AV_PIX_FMT_RGB444 AV_PIX_FMT_NE(RGB444BE, RGB444LE)
#define AV_PIX_FMT_RGBA64 AV_PIX_FMT_NE(RGBA64BE, RGBA64LE)
#define AV_PIX_FMT_BGR48 AV_PIX_FMT_NE(BGR48BE, BGR48LE)
#define AV_PIX_FMT_BGR565 AV_PIX_FMT_NE(BGR565BE, BGR565LE)
#define AV_PIX_FMT_BGR555 AV_PIX_FMT_NE(BGR555BE, BGR555LE)
#define AV_PIX_FMT_BGR444 AV_PIX_FMT_NE(BGR444BE, BGR444LE)
#define AV_PIX_FMT_BGRA64 AV_PIX_FMT_NE(BGRA64BE, BGRA64LE)
#define AV_PIX_FMT_YUV420P9 AV_PIX_FMT_NE(YUV420P9BE , YUV420P9LE)
#define AV_PIX_FMT_YUV422P9 AV_PIX_FMT_NE(YUV422P9BE , YUV422P9LE)
#define AV_PIX_FMT_YUV444P9 AV_PIX_FMT_NE(YUV444P9BE , YUV444P9LE)
#define AV_PIX_FMT_YUV420P10 AV_PIX_FMT_NE(YUV420P10BE, YUV420P10LE)
#define AV_PIX_FMT_YUV422P10 AV_PIX_FMT_NE(YUV422P10BE, YUV422P10LE)
#define AV_PIX_FMT_YUV440P10 AV_PIX_FMT_NE(YUV440P10BE, YUV440P10LE)
#define AV_PIX_FMT_YUV444P10 AV_PIX_FMT_NE(YUV444P10BE, YUV444P10LE)
#define AV_PIX_FMT_YUV420P12 AV_PIX_FMT_NE(YUV420P12BE, YUV420P12LE)
#define AV_PIX_FMT_YUV422P12 AV_PIX_FMT_NE(YUV422P12BE, YUV422P12LE)
#define AV_PIX_FMT_YUV440P12 AV_PIX_FMT_NE(YUV440P12BE, YUV440P12LE)
#define AV_PIX_FMT_YUV444P12 AV_PIX_FMT_NE(YUV444P12BE, YUV444P12LE)
#define AV_PIX_FMT_YUV420P14 AV_PIX_FMT_NE(YUV420P14BE, YUV420P14LE)
#define AV_PIX_FMT_YUV422P14 AV_PIX_FMT_NE(YUV422P14BE, YUV422P14LE)
#define AV_PIX_FMT_YUV444P14 AV_PIX_FMT_NE(YUV444P14BE, YUV444P14LE)
#define AV_PIX_FMT_YUV420P16 AV_PIX_FMT_NE(YUV420P16BE, YUV420P16LE)
#define AV_PIX_FMT_YUV422P16 AV_PIX_FMT_NE(YUV422P16BE, YUV422P16LE)
#define AV_PIX_FMT_YUV444P16 AV_PIX_FMT_NE(YUV444P16BE, YUV444P16LE)
#define AV_PIX_FMT_GBRP9 AV_PIX_FMT_NE(GBRP9BE , GBRP9LE)
#define AV_PIX_FMT_GBRP10 AV_PIX_FMT_NE(GBRP10BE, GBRP10LE)
#define AV_PIX_FMT_GBRP12 AV_PIX_FMT_NE(GBRP12BE, GBRP12LE)
#define AV_PIX_FMT_GBRP14 AV_PIX_FMT_NE(GBRP14BE, GBRP14LE)
#define AV_PIX_FMT_GBRP16 AV_PIX_FMT_NE(GBRP16BE, GBRP16LE)
#define AV_PIX_FMT_GBRAP10 AV_PIX_FMT_NE(GBRAP10BE, GBRAP10LE)
#define AV_PIX_FMT_GBRAP12 AV_PIX_FMT_NE(GBRAP12BE, GBRAP12LE)
#define AV_PIX_FMT_GBRAP16 AV_PIX_FMT_NE(GBRAP16BE, GBRAP16LE)
#define AV_PIX_FMT_BAYER_BGGR16 AV_PIX_FMT_NE(BAYER_BGGR16BE, BAYER_BGGR16LE)
#define AV_PIX_FMT_BAYER_RGGB16 AV_PIX_FMT_NE(BAYER_RGGB16BE, BAYER_RGGB16LE)
#define AV_PIX_FMT_BAYER_GBRG16 AV_PIX_FMT_NE(BAYER_GBRG16BE, BAYER_GBRG16LE)
#define AV_PIX_FMT_BAYER_GRBG16 AV_PIX_FMT_NE(BAYER_GRBG16BE, BAYER_GRBG16LE)
#define AV_PIX_FMT_GBRPF32 AV_PIX_FMT_NE(GBRPF32BE, GBRPF32LE)
#define AV_PIX_FMT_GBRAPF32 AV_PIX_FMT_NE(GBRAPF32BE, GBRAPF32LE)
#define AV_PIX_FMT_GRAYF32 AV_PIX_FMT_NE(GRAYF32BE, GRAYF32LE)
#define AV_PIX_FMT_YUVA420P9 AV_PIX_FMT_NE(YUVA420P9BE , YUVA420P9LE)
#define AV_PIX_FMT_YUVA422P9 AV_PIX_FMT_NE(YUVA422P9BE , YUVA422P9LE)
#define AV_PIX_FMT_YUVA444P9 AV_PIX_FMT_NE(YUVA444P9BE , YUVA444P9LE)
#define AV_PIX_FMT_YUVA420P10 AV_PIX_FMT_NE(YUVA420P10BE, YUVA420P10LE)
#define AV_PIX_FMT_YUVA422P10 AV_PIX_FMT_NE(YUVA422P10BE, YUVA422P10LE)
#define AV_PIX_FMT_YUVA444P10 AV_PIX_FMT_NE(YUVA444P10BE, YUVA444P10LE)
#define AV_PIX_FMT_YUVA422P12 AV_PIX_FMT_NE(YUVA422P12BE, YUVA422P12LE)
#define AV_PIX_FMT_YUVA444P12 AV_PIX_FMT_NE(YUVA444P12BE, YUVA444P12LE)
#define AV_PIX_FMT_YUVA420P16 AV_PIX_FMT_NE(YUVA420P16BE, YUVA420P16LE)
#define AV_PIX_FMT_YUVA422P16 AV_PIX_FMT_NE(YUVA422P16BE, YUVA422P16LE)
#define AV_PIX_FMT_YUVA444P16 AV_PIX_FMT_NE(YUVA444P16BE, YUVA444P16LE)
#define AV_PIX_FMT_XYZ12 AV_PIX_FMT_NE(XYZ12BE, XYZ12LE)
#define AV_PIX_FMT_NV20 AV_PIX_FMT_NE(NV20BE, NV20LE)
#define AV_PIX_FMT_AYUV64 AV_PIX_FMT_NE(AYUV64BE, AYUV64LE)
#define AV_PIX_FMT_P010 AV_PIX_FMT_NE(P010BE, P010LE)
#define AV_PIX_FMT_P012 AV_PIX_FMT_NE(P012BE, P012LE)
#define AV_PIX_FMT_P016 AV_PIX_FMT_NE(P016BE, P016LE)
#define AV_PIX_FMT_Y210 AV_PIX_FMT_NE(Y210BE, Y210LE)
#define AV_PIX_FMT_Y212 AV_PIX_FMT_NE(Y212BE, Y212LE)
#define AV_PIX_FMT_XV30 AV_PIX_FMT_NE(XV30BE, XV30LE)
#define AV_PIX_FMT_XV36 AV_PIX_FMT_NE(XV36BE, XV36LE)
#define AV_PIX_FMT_X2RGB10 AV_PIX_FMT_NE(X2RGB10BE, X2RGB10LE)
#define AV_PIX_FMT_X2BGR10 AV_PIX_FMT_NE(X2BGR10BE, X2BGR10LE)
#define AV_PIX_FMT_P210 AV_PIX_FMT_NE(P210BE, P210LE)
#define AV_PIX_FMT_P410 AV_PIX_FMT_NE(P410BE, P410LE)
#define AV_PIX_FMT_P212 AV_PIX_FMT_NE(P212BE, P212LE)
#define AV_PIX_FMT_P412 AV_PIX_FMT_NE(P412BE, P412LE)
#define AV_PIX_FMT_P216 AV_PIX_FMT_NE(P216BE, P216LE)
#define AV_PIX_FMT_P416 AV_PIX_FMT_NE(P416BE, P416LE)
#define AV_PIX_FMT_RGBAF16 AV_PIX_FMT_NE(RGBAF16BE, RGBAF16LE)
#define AV_PIX_FMT_RGBF32 AV_PIX_FMT_NE(RGBF32BE, RGBF32LE)
#define AV_PIX_FMT_RGBAF32 AV_PIX_FMT_NE(RGBAF32BE, RGBAF32LE)
/**
* Chromaticity coordinates of the source primaries.
* These values match the ones defined by ISO/IEC 23091-2_2019 subclause 8.1 and ITU-T H.273.
*/
enum AVColorPrimaries {
AVCOL_PRI_RESERVED0 = 0,
AVCOL_PRI_BT709 = 1, ///< also ITU-R BT1361 / IEC 61966-2-4 / SMPTE RP 177 Annex B
AVCOL_PRI_UNSPECIFIED = 2,
AVCOL_PRI_RESERVED = 3,
AVCOL_PRI_BT470M = 4, ///< also FCC Title 47 Code of Federal Regulations 73.682 (a)(20)
AVCOL_PRI_BT470BG = 5, ///< also ITU-R BT601-6 625 / ITU-R BT1358 625 / ITU-R BT1700 625 PAL & SECAM
AVCOL_PRI_SMPTE170M = 6, ///< also ITU-R BT601-6 525 / ITU-R BT1358 525 / ITU-R BT1700 NTSC
AVCOL_PRI_SMPTE240M = 7, ///< identical to above, also called "SMPTE C" even though it uses D65
AVCOL_PRI_FILM = 8, ///< colour filters using Illuminant C
AVCOL_PRI_BT2020 = 9, ///< ITU-R BT2020
AVCOL_PRI_SMPTE428 = 10, ///< SMPTE ST 428-1 (CIE 1931 XYZ)
AVCOL_PRI_SMPTEST428_1 = AVCOL_PRI_SMPTE428,
AVCOL_PRI_SMPTE431 = 11, ///< SMPTE ST 431-2 (2011) / DCI P3
AVCOL_PRI_SMPTE432 = 12, ///< SMPTE ST 432-1 (2010) / P3 D65 / Display P3
AVCOL_PRI_EBU3213 = 22, ///< EBU Tech. 3213-E (nothing there) / one of JEDEC P22 group phosphors
AVCOL_PRI_JEDEC_P22 = AVCOL_PRI_EBU3213,
AVCOL_PRI_NB ///< Not part of ABI
};
/**
* Color Transfer Characteristic.
* These values match the ones defined by ISO/IEC 23091-2_2019 subclause 8.2.
*/
enum AVColorTransferCharacteristic {
AVCOL_TRC_RESERVED0 = 0,
AVCOL_TRC_BT709 = 1, ///< also ITU-R BT1361
AVCOL_TRC_UNSPECIFIED = 2,
AVCOL_TRC_RESERVED = 3,
AVCOL_TRC_GAMMA22 = 4, ///< also ITU-R BT470M / ITU-R BT1700 625 PAL & SECAM
AVCOL_TRC_GAMMA28 = 5, ///< also ITU-R BT470BG
AVCOL_TRC_SMPTE170M = 6, ///< also ITU-R BT601-6 525 or 625 / ITU-R BT1358 525 or 625 / ITU-R BT1700 NTSC
AVCOL_TRC_SMPTE240M = 7,
AVCOL_TRC_LINEAR = 8, ///< "Linear transfer characteristics"
AVCOL_TRC_LOG = 9, ///< "Logarithmic transfer characteristic (100:1 range)"
AVCOL_TRC_LOG_SQRT = 10, ///< "Logarithmic transfer characteristic (100 * Sqrt(10) : 1 range)"
AVCOL_TRC_IEC61966_2_4 = 11, ///< IEC 61966-2-4
AVCOL_TRC_BT1361_ECG = 12, ///< ITU-R BT1361 Extended Colour Gamut
AVCOL_TRC_IEC61966_2_1 = 13, ///< IEC 61966-2-1 (sRGB or sYCC)
AVCOL_TRC_BT2020_10 = 14, ///< ITU-R BT2020 for 10-bit system
AVCOL_TRC_BT2020_12 = 15, ///< ITU-R BT2020 for 12-bit system
AVCOL_TRC_SMPTE2084 = 16, ///< SMPTE ST 2084 for 10-, 12-, 14- and 16-bit systems
AVCOL_TRC_SMPTEST2084 = AVCOL_TRC_SMPTE2084,
AVCOL_TRC_SMPTE428 = 17, ///< SMPTE ST 428-1
AVCOL_TRC_SMPTEST428_1 = AVCOL_TRC_SMPTE428,
AVCOL_TRC_ARIB_STD_B67 = 18, ///< ARIB STD-B67, known as "Hybrid log-gamma"
AVCOL_TRC_NB ///< Not part of ABI
};
/**
* YUV colorspace type.
* These values match the ones defined by ISO/IEC 23091-2_2019 subclause 8.3.
*/
enum AVColorSpace {
AVCOL_SPC_RGB = 0, ///< order of coefficients is actually GBR, also IEC 61966-2-1 (sRGB), YZX and ST 428-1
AVCOL_SPC_BT709 = 1, ///< also ITU-R BT1361 / IEC 61966-2-4 xvYCC709 / derived in SMPTE RP 177 Annex B
AVCOL_SPC_UNSPECIFIED = 2,
AVCOL_SPC_RESERVED = 3, ///< reserved for future use by ITU-T and ISO/IEC just like 15-255 are
AVCOL_SPC_FCC = 4, ///< FCC Title 47 Code of Federal Regulations 73.682 (a)(20)
AVCOL_SPC_BT470BG = 5, ///< also ITU-R BT601-6 625 / ITU-R BT1358 625 / ITU-R BT1700 625 PAL & SECAM / IEC 61966-2-4 xvYCC601
AVCOL_SPC_SMPTE170M = 6, ///< also ITU-R BT601-6 525 / ITU-R BT1358 525 / ITU-R BT1700 NTSC / functionally identical to above
AVCOL_SPC_SMPTE240M = 7, ///< derived from 170M primaries and D65 white point, 170M is derived from BT470 System M's primaries
AVCOL_SPC_YCGCO = 8, ///< used by Dirac / VC-2 and H.264 FRext, see ITU-T SG16
AVCOL_SPC_YCOCG = AVCOL_SPC_YCGCO,
AVCOL_SPC_BT2020_NCL = 9, ///< ITU-R BT2020 non-constant luminance system
AVCOL_SPC_BT2020_CL = 10, ///< ITU-R BT2020 constant luminance system
AVCOL_SPC_SMPTE2085 = 11, ///< SMPTE 2085, Y'D'zD'x
AVCOL_SPC_CHROMA_DERIVED_NCL = 12, ///< Chromaticity-derived non-constant luminance system
AVCOL_SPC_CHROMA_DERIVED_CL = 13, ///< Chromaticity-derived constant luminance system
AVCOL_SPC_ICTCP = 14, ///< ITU-R BT.2100-0, ICtCp
AVCOL_SPC_NB ///< Not part of ABI
};
/**
* Visual content value range.
*
* These values are based on definitions that can be found in multiple
* specifications, such as ITU-T BT.709 (3.4 - Quantization of RGB, luminance
* and colour-difference signals), ITU-T BT.2020 (Table 5 - Digital
* Representation) as well as ITU-T BT.2100 (Table 9 - Digital 10- and 12-bit
* integer representation). At the time of writing, the BT.2100 one is
* recommended, as it also defines the full range representation.
*
* Common definitions:
* - For RGB and luma planes such as Y in YCbCr and I in ICtCp,
* 'E' is the original value in range of 0.0 to 1.0.
* - For chroma planes such as Cb,Cr and Ct,Cp, 'E' is the original
* value in range of -0.5 to 0.5.
* - 'n' is the output bit depth.
* - For additional definitions such as rounding and clipping to valid n
* bit unsigned integer range, please refer to BT.2100 (Table 9).
*/
enum AVColorRange {
AVCOL_RANGE_UNSPECIFIED = 0,
/**
* Narrow or limited range content.
*
* - For luma planes:
*
* (219 * E + 16) * 2^(n-8)
*
* F.ex. the range of 16-235 for 8 bits
*
* - For chroma planes:
*
* (224 * E + 128) * 2^(n-8)
*
* F.ex. the range of 16-240 for 8 bits
*/
AVCOL_RANGE_MPEG = 1,
/**
* Full range content.
*
* - For RGB and luma planes:
*
* (2^n - 1) * E
*
* F.ex. the range of 0-255 for 8 bits
*
* - For chroma planes:
*
* (2^n - 1) * E + 2^(n - 1)
*
* F.ex. the range of 1-255 for 8 bits
*/
AVCOL_RANGE_JPEG = 2,
AVCOL_RANGE_NB ///< Not part of ABI
};
/**
* Location of chroma samples.
*
* Illustration showing the location of the first (top left) chroma sample of the
* image, the left shows only luma, the right
* shows the location of the chroma sample, the 2 could be imagined to overlay
* each other but are drawn separately due to limitations of ASCII
*
* 1st 2nd 1st 2nd horizontal luma sample positions
* v v v v
* ______ ______
*1st luma line > |X X ... |3 4 X ... X are luma samples,
* | |1 2 1-6 are possible chroma positions
*2nd luma line > |X X ... |5 6 X ... 0 is undefined/unknown position
*/
enum AVChromaLocation {
AVCHROMA_LOC_UNSPECIFIED = 0,
AVCHROMA_LOC_LEFT = 1, ///< MPEG-2/4 4:2:0, H.264 default for 4:2:0
AVCHROMA_LOC_CENTER = 2, ///< MPEG-1 4:2:0, JPEG 4:2:0, H.263 4:2:0
AVCHROMA_LOC_TOPLEFT = 3, ///< ITU-R 601, SMPTE 274M 296M S314M(DV 4:1:1), mpeg2 4:2:2
AVCHROMA_LOC_TOP = 4,
AVCHROMA_LOC_BOTTOMLEFT = 5,
AVCHROMA_LOC_BOTTOM = 6,
AVCHROMA_LOC_NB ///< Not part of ABI
};
#endif /* AVUTIL_PIXFMT_H */

View File

@ -0,0 +1,193 @@
/*
* rational numbers
* Copyright (c) 2003 Michael Niedermayer <michaelni@gmx.at>
*
* 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
* rational numbers
* @author Michael Niedermayer <michaelni@gmx.at>
*/
#include "avassert.h"
#include <limits.h>
#include "common.h"
#include "mathematics.h"
#include "rational.h"
int av_reduce(int *dst_num, int *dst_den,
int64_t num, int64_t den, int64_t max)
{
AVRational a0 = { 0, 1 }, a1 = { 1, 0 };
int sign = (num < 0) ^ (den < 0);
int64_t gcd = av_gcd(FFABS(num), FFABS(den));
if (gcd) {
num = FFABS(num) / gcd;
den = FFABS(den) / gcd;
}
if (num <= max && den <= max) {
a1 = (AVRational) { num, den };
den = 0;
}
while (den) {
uint64_t x = num / den;
int64_t next_den = num - den * x;
int64_t a2n = x * a1.num + a0.num;
int64_t a2d = x * a1.den + a0.den;
if (a2n > max || a2d > max) {
if (a1.num) x = (max - a0.num) / a1.num;
if (a1.den) x = FFMIN(x, (max - a0.den) / a1.den);
if (den * (2 * x * a1.den + a0.den) > num * a1.den)
a1 = (AVRational) { x * a1.num + a0.num, x * a1.den + a0.den };
break;
}
a0 = a1;
a1 = (AVRational) { a2n, a2d };
num = den;
den = next_den;
}
av_assert2(av_gcd(a1.num, a1.den) <= 1U);
av_assert2(a1.num <= max && a1.den <= max);
*dst_num = sign ? -a1.num : a1.num;
*dst_den = a1.den;
return den == 0;
}
AVRational av_mul_q(AVRational b, AVRational c)
{
av_reduce(&b.num, &b.den,
b.num * (int64_t) c.num,
b.den * (int64_t) c.den, INT_MAX);
return b;
}
AVRational av_div_q(AVRational b, AVRational c)
{
return av_mul_q(b, (AVRational) { c.den, c.num });
}
AVRational av_add_q(AVRational b, AVRational c) {
av_reduce(&b.num, &b.den,
b.num * (int64_t) c.den +
c.num * (int64_t) b.den,
b.den * (int64_t) c.den, INT_MAX);
return b;
}
AVRational av_sub_q(AVRational b, AVRational c)
{
return av_add_q(b, (AVRational) { -c.num, c.den });
}
AVRational av_d2q(double d, int max)
{
AVRational a;
int exponent;
int64_t den;
if (isnan(d))
return (AVRational) { 0,0 };
if (fabs(d) > INT_MAX + 3LL)
return (AVRational) { d < 0 ? -1 : 1, 0 };
frexp(d, &exponent);
exponent = FFMAX(exponent-1, 0);
den = 1LL << (61 - exponent);
// (int64_t)rint() and llrint() do not work with gcc on ia64 and sparc64,
// see Ticket2713 for affected gcc/glibc versions
av_reduce(&a.num, &a.den, floor(d * den + 0.5), den, max);
if ((!a.num || !a.den) && d && max>0 && max<INT_MAX)
av_reduce(&a.num, &a.den, floor(d * den + 0.5), den, INT_MAX);
return a;
}
int av_nearer_q(AVRational q, AVRational q1, AVRational q2)
{
/* n/d is q, a/b is the median between q1 and q2 */
int64_t a = q1.num * (int64_t)q2.den + q2.num * (int64_t)q1.den;
int64_t b = 2 * (int64_t)q1.den * q2.den;
/* rnd_up(a*d/b) > n => a*d/b > n */
int64_t x_up = av_rescale_rnd(a, q.den, b, AV_ROUND_UP);
/* rnd_down(a*d/b) < n => a*d/b < n */
int64_t x_down = av_rescale_rnd(a, q.den, b, AV_ROUND_DOWN);
return ((x_up > q.num) - (x_down < q.num)) * av_cmp_q(q2, q1);
}
int av_find_nearest_q_idx(AVRational q, const AVRational* q_list)
{
int i, nearest_q_idx = 0;
for (i = 0; q_list[i].den; i++)
if (av_nearer_q(q, q_list[i], q_list[nearest_q_idx]) > 0)
nearest_q_idx = i;
return nearest_q_idx;
}
uint32_t av_q2intfloat(AVRational q) {
int64_t n;
int shift;
int sign = 0;
if (q.den < 0) {
q.den *= -1;
q.num *= -1;
}
if (q.num < 0) {
q.num *= -1;
sign = 1;
}
if (!q.num && !q.den) return 0xFFC00000;
if (!q.num) return 0;
if (!q.den) return 0x7F800000 | (q.num & 0x80000000);
shift = 23 + av_log2(q.den) - av_log2(q.num);
if (shift >= 0) n = av_rescale(q.num, 1LL<<shift, q.den);
else n = av_rescale(q.num, 1, ((int64_t)q.den) << -shift);
shift -= n >= (1<<24);
shift += n < (1<<23);
if (shift >= 0) n = av_rescale(q.num, 1LL<<shift, q.den);
else n = av_rescale(q.num, 1, ((int64_t)q.den) << -shift);
av_assert1(n < (1<<24));
av_assert1(n >= (1<<23));
return sign<<31 | (150-shift)<<23 | (n - (1<<23));
}
AVRational av_gcd_q(AVRational a, AVRational b, int max_den, AVRational def)
{
int64_t gcd, lcm;
gcd = av_gcd(a.den, b.den);
lcm = (a.den / gcd) * b.den;
return lcm < max_den ? av_make_q(av_gcd(a.num, b.num), lcm) : def;
}

View File

@ -0,0 +1,221 @@
/*
* rational numbers
* Copyright (c) 2003 Michael Niedermayer <michaelni@gmx.at>
*
* 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
* @ingroup lavu_math_rational
* Utilties for rational number calculation.
* @author Michael Niedermayer <michaelni@gmx.at>
*/
#ifndef AVUTIL_RATIONAL_H
#define AVUTIL_RATIONAL_H
#include <stdint.h>
#include <limits.h>
#include "attributes.h"
/**
* @defgroup lavu_math_rational AVRational
* @ingroup lavu_math
* Rational number calculation.
*
* While rational numbers can be expressed as floating-point numbers, the
* conversion process is a lossy one, so are floating-point operations. On the
* other hand, the nature of FFmpeg demands highly accurate calculation of
* timestamps. This set of rational number utilities serves as a generic
* interface for manipulating rational numbers as pairs of numerators and
* denominators.
*
* Many of the functions that operate on AVRational's have the suffix `_q`, in
* reference to the mathematical symbol "" (Q) which denotes the set of all
* rational numbers.
*
* @{
*/
/**
* Rational number (pair of numerator and denominator).
*/
typedef struct AVRational{
int num; ///< Numerator
int den; ///< Denominator
} AVRational;
/**
* Create an AVRational.
*
* Useful for compilers that do not support compound literals.
*
* @note The return value is not reduced.
* @see av_reduce()
*/
static inline AVRational av_make_q(int num, int den)
{
AVRational r = { num, den };
return r;
}
/**
* Compare two rationals.
*
* @param a First rational
* @param b Second rational
*
* @return One of the following values:
* - 0 if `a == b`
* - 1 if `a > b`
* - -1 if `a < b`
* - `INT_MIN` if one of the values is of the form `0 / 0`
*/
static inline int av_cmp_q(AVRational a, AVRational b){
const int64_t tmp= a.num * (int64_t)b.den - b.num * (int64_t)a.den;
if(tmp) return (int)((tmp ^ a.den ^ b.den)>>63)|1;
else if(b.den && a.den) return 0;
else if(a.num && b.num) return (a.num>>31) - (b.num>>31);
else return INT_MIN;
}
/**
* Convert an AVRational to a `double`.
* @param a AVRational to convert
* @return `a` in floating-point form
* @see av_d2q()
*/
static inline double av_q2d(AVRational a){
return a.num / (double) a.den;
}
/**
* Reduce a fraction.
*
* This is useful for framerate calculations.
*
* @param[out] dst_num Destination numerator
* @param[out] dst_den Destination denominator
* @param[in] num Source numerator
* @param[in] den Source denominator
* @param[in] max Maximum allowed values for `dst_num` & `dst_den`
* @return 1 if the operation is exact, 0 otherwise
*/
int av_reduce(int *dst_num, int *dst_den, int64_t num, int64_t den, int64_t max);
/**
* Multiply two rationals.
* @param b First rational
* @param c Second rational
* @return b*c
*/
AVRational av_mul_q(AVRational b, AVRational c) av_const;
/**
* Divide one rational by another.
* @param b First rational
* @param c Second rational
* @return b/c
*/
AVRational av_div_q(AVRational b, AVRational c) av_const;
/**
* Add two rationals.
* @param b First rational
* @param c Second rational
* @return b+c
*/
AVRational av_add_q(AVRational b, AVRational c) av_const;
/**
* Subtract one rational from another.
* @param b First rational
* @param c Second rational
* @return b-c
*/
AVRational av_sub_q(AVRational b, AVRational c) av_const;
/**
* Invert a rational.
* @param q value
* @return 1 / q
*/
static av_always_inline AVRational av_inv_q(AVRational q)
{
AVRational r = { q.den, q.num };
return r;
}
/**
* Convert a double precision floating point number to a rational.
*
* In case of infinity, the returned value is expressed as `{1, 0}` or
* `{-1, 0}` depending on the sign.
*
* @param d `double` to convert
* @param max Maximum allowed numerator and denominator
* @return `d` in AVRational form
* @see av_q2d()
*/
AVRational av_d2q(double d, int max) av_const;
/**
* Find which of the two rationals is closer to another rational.
*
* @param q Rational to be compared against
* @param q1 Rational to be tested
* @param q2 Rational to be tested
* @return One of the following values:
* - 1 if `q1` is nearer to `q` than `q2`
* - -1 if `q2` is nearer to `q` than `q1`
* - 0 if they have the same distance
*/
int av_nearer_q(AVRational q, AVRational q1, AVRational q2);
/**
* Find the value in a list of rationals nearest a given reference rational.
*
* @param q Reference rational
* @param q_list Array of rationals terminated by `{0, 0}`
* @return Index of the nearest value found in the array
*/
int av_find_nearest_q_idx(AVRational q, const AVRational* q_list);
/**
* Convert an AVRational to a IEEE 32-bit `float` expressed in fixed-point
* format.
*
* @param q Rational to be converted
* @return Equivalent floating-point value, expressed as an unsigned 32-bit
* integer.
* @note The returned value is platform-indepedant.
*/
uint32_t av_q2intfloat(AVRational q);
/**
* Return the best rational so that a and b are multiple of it.
* If the resulting denominator is larger than max_den, return def.
*/
AVRational av_gcd_q(AVRational a, AVRational b, int max_den, AVRational def);
/**
* @}
*/
#endif /* AVUTIL_RATIONAL_H */

View File

@ -0,0 +1,204 @@
/*
* 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
*/
// This header should only be used to simplify code where
// threading is optional, not as a generic threading abstraction.
#ifndef AVUTIL_THREAD_H
#define AVUTIL_THREAD_H
#include "config.h"
#if HAVE_PRCTL
#include <sys/prctl.h>
#endif
#include "error.h"
#if HAVE_PTHREADS || HAVE_W32THREADS || HAVE_OS2THREADS
#if HAVE_PTHREADS
#include <pthread.h>
#if defined(ASSERT_LEVEL) && ASSERT_LEVEL > 1
#include <stdlib.h>
#include "log.h"
#include "macros.h"
#define ASSERT_PTHREAD_ABORT(func, ret) do { \
char errbuf[AV_ERROR_MAX_STRING_SIZE] = ""; \
av_log(NULL, AV_LOG_FATAL, AV_STRINGIFY(func) \
" failed with error: %s\n", \
av_make_error_string(errbuf, AV_ERROR_MAX_STRING_SIZE, \
AVERROR(ret))); \
abort(); \
} while (0)
#define ASSERT_PTHREAD_NORET(func, ...) do { \
int ret = func(__VA_ARGS__); \
if (ret) \
ASSERT_PTHREAD_ABORT(func, ret); \
} while (0)
#define ASSERT_PTHREAD(func, ...) do { \
ASSERT_PTHREAD_NORET(func, __VA_ARGS__); \
return 0; \
} while (0)
static inline int strict_pthread_join(pthread_t thread, void **value_ptr)
{
ASSERT_PTHREAD(pthread_join, thread, value_ptr);
}
static inline int strict_pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)
{
if (attr) {
ASSERT_PTHREAD_NORET(pthread_mutex_init, mutex, attr);
} else {
pthread_mutexattr_t local_attr;
ASSERT_PTHREAD_NORET(pthread_mutexattr_init, &local_attr);
ASSERT_PTHREAD_NORET(pthread_mutexattr_settype, &local_attr, PTHREAD_MUTEX_ERRORCHECK);
ASSERT_PTHREAD_NORET(pthread_mutex_init, mutex, &local_attr);
ASSERT_PTHREAD_NORET(pthread_mutexattr_destroy, &local_attr);
}
return 0;
}
static inline int strict_pthread_mutex_destroy(pthread_mutex_t *mutex)
{
ASSERT_PTHREAD(pthread_mutex_destroy, mutex);
}
static inline int strict_pthread_mutex_lock(pthread_mutex_t *mutex)
{
ASSERT_PTHREAD(pthread_mutex_lock, mutex);
}
static inline int strict_pthread_mutex_unlock(pthread_mutex_t *mutex)
{
ASSERT_PTHREAD(pthread_mutex_unlock, mutex);
}
static inline int strict_pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr)
{
ASSERT_PTHREAD(pthread_cond_init, cond, attr);
}
static inline int strict_pthread_cond_destroy(pthread_cond_t *cond)
{
ASSERT_PTHREAD(pthread_cond_destroy, cond);
}
static inline int strict_pthread_cond_signal(pthread_cond_t *cond)
{
ASSERT_PTHREAD(pthread_cond_signal, cond);
}
static inline int strict_pthread_cond_broadcast(pthread_cond_t *cond)
{
ASSERT_PTHREAD(pthread_cond_broadcast, cond);
}
static inline int strict_pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
{
ASSERT_PTHREAD(pthread_cond_wait, cond, mutex);
}
static inline int strict_pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
const struct timespec *abstime)
{
int ret = pthread_cond_timedwait(cond, mutex, abstime);
if (ret && ret != ETIMEDOUT)
ASSERT_PTHREAD_ABORT(pthread_cond_timedwait, ret);
return ret;
}
static inline int strict_pthread_once(pthread_once_t *once_control, void (*init_routine)(void))
{
ASSERT_PTHREAD(pthread_once, once_control, init_routine);
}
#define pthread_join strict_pthread_join
#define pthread_mutex_init strict_pthread_mutex_init
#define pthread_mutex_destroy strict_pthread_mutex_destroy
#define pthread_mutex_lock strict_pthread_mutex_lock
#define pthread_mutex_unlock strict_pthread_mutex_unlock
#define pthread_cond_init strict_pthread_cond_init
#define pthread_cond_destroy strict_pthread_cond_destroy
#define pthread_cond_signal strict_pthread_cond_signal
#define pthread_cond_broadcast strict_pthread_cond_broadcast
#define pthread_cond_wait strict_pthread_cond_wait
#define pthread_cond_timedwait strict_pthread_cond_timedwait
#define pthread_once strict_pthread_once
#endif
#elif HAVE_OS2THREADS
#include "compat/os2threads.h"
#else
#include "compat/w32pthreads.h"
#endif
#define AVMutex pthread_mutex_t
#define AV_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER
#define ff_mutex_init pthread_mutex_init
#define ff_mutex_lock pthread_mutex_lock
#define ff_mutex_unlock pthread_mutex_unlock
#define ff_mutex_destroy pthread_mutex_destroy
#define AVOnce pthread_once_t
#define AV_ONCE_INIT PTHREAD_ONCE_INIT
#define ff_thread_once(control, routine) pthread_once(control, routine)
#else
#define AVMutex char
#define AV_MUTEX_INITIALIZER 0
static inline int ff_mutex_init(AVMutex *mutex, const void *attr){ return 0; }
static inline int ff_mutex_lock(AVMutex *mutex){ return 0; }
static inline int ff_mutex_unlock(AVMutex *mutex){ return 0; }
static inline int ff_mutex_destroy(AVMutex *mutex){ return 0; }
#define AVOnce char
#define AV_ONCE_INIT 0
static inline int ff_thread_once(char *control, void (*routine)(void))
{
if (!*control) {
routine();
*control = 1;
}
return 0;
}
#endif
static inline int ff_thread_setname(const char *name)
{
#if HAVE_PRCTL
return AVERROR(prctl(PR_SET_NAME, name));
#endif
return AVERROR(ENOSYS);
}
#endif /* AVUTIL_THREAD_H */

View File

@ -0,0 +1,128 @@
/*
* copyright (c) 2003 Fabrice Bellard
*
* 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
* @ingroup lavu
* Libavutil version macros
*/
#ifndef AVUTIL_VERSION_H
#define AVUTIL_VERSION_H
#include "macros.h"
/**
* @addtogroup version_utils
*
* Useful to check and match library version in order to maintain
* backward compatibility.
*
* The FFmpeg libraries follow a versioning sheme very similar to
* Semantic Versioning (http://semver.org/)
* The difference is that the component called PATCH is called MICRO in FFmpeg
* and its value is reset to 100 instead of 0 to keep it above or equal to 100.
* Also we do not increase MICRO for every bugfix or change in git master.
*
* Prior to FFmpeg 3.2 point releases did not change any lib version number to
* avoid aliassing different git master checkouts.
* Starting with FFmpeg 3.2, the released library versions will occupy
* a separate MAJOR.MINOR that is not used on the master development branch.
* That is if we branch a release of master 55.10.123 we will bump to 55.11.100
* for the release and master will continue at 55.12.100 after it. Each new
* point release will then bump the MICRO improving the usefulness of the lib
* versions.
*
* @{
*/
#define AV_VERSION_INT(a, b, c) ((a)<<16 | (b)<<8 | (c))
#define AV_VERSION_DOT(a, b, c) a ##.## b ##.## c
#define AV_VERSION(a, b, c) AV_VERSION_DOT(a, b, c)
/**
* Extract version components from the full ::AV_VERSION_INT int as returned
* by functions like ::avformat_version() and ::avcodec_version()
*/
#define AV_VERSION_MAJOR(a) ((a) >> 16)
#define AV_VERSION_MINOR(a) (((a) & 0x00FF00) >> 8)
#define AV_VERSION_MICRO(a) ((a) & 0xFF)
/**
* @}
*/
/**
* @defgroup lavu_ver Version and Build diagnostics
*
* Macros and function useful to check at compiletime and at runtime
* which version of libavutil is in use.
*
* @{
*/
#define LIBAVUTIL_VERSION_MAJOR 58
#define LIBAVUTIL_VERSION_MINOR 12
#define LIBAVUTIL_VERSION_MICRO 100
#define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \
LIBAVUTIL_VERSION_MINOR, \
LIBAVUTIL_VERSION_MICRO)
#define LIBAVUTIL_VERSION AV_VERSION(LIBAVUTIL_VERSION_MAJOR, \
LIBAVUTIL_VERSION_MINOR, \
LIBAVUTIL_VERSION_MICRO)
#define LIBAVUTIL_BUILD LIBAVUTIL_VERSION_INT
#define LIBAVUTIL_IDENT "Lavu" AV_STRINGIFY(LIBAVUTIL_VERSION)
/**
* @defgroup lavu_depr_guards Deprecation Guards
* FF_API_* defines may be placed below to indicate public API that will be
* dropped at a future version bump. The defines themselves are not part of
* the public API and may change, break or disappear at any time.
*
* @note, when bumping the major version it is recommended to manually
* disable each FF_API_* in its own commit instead of disabling them all
* at once through the bump. This improves the git bisect-ability of the change.
*
* @{
*/
#define FF_API_FIFO_PEEK2 (LIBAVUTIL_VERSION_MAJOR < 59)
#define FF_API_FIFO_OLD_API (LIBAVUTIL_VERSION_MAJOR < 59)
#define FF_API_XVMC (LIBAVUTIL_VERSION_MAJOR < 59)
#define FF_API_OLD_CHANNEL_LAYOUT (LIBAVUTIL_VERSION_MAJOR < 59)
#define FF_API_AV_FOPEN_UTF8 (LIBAVUTIL_VERSION_MAJOR < 59)
#define FF_API_PKT_DURATION (LIBAVUTIL_VERSION_MAJOR < 59)
#define FF_API_REORDERED_OPAQUE (LIBAVUTIL_VERSION_MAJOR < 59)
#define FF_API_FRAME_PICTURE_NUMBER (LIBAVUTIL_VERSION_MAJOR < 59)
#define FF_API_HDR_VIVID_THREE_SPLINE (LIBAVUTIL_VERSION_MAJOR < 59)
#define FF_API_FRAME_PKT (LIBAVUTIL_VERSION_MAJOR < 59)
#define FF_API_INTERLACED_FRAME (LIBAVUTIL_VERSION_MAJOR < 59)
#define FF_API_FRAME_KEY (LIBAVUTIL_VERSION_MAJOR < 59)
#define FF_API_PALETTE_HAS_CHANGED (LIBAVUTIL_VERSION_MAJOR < 59)
#define FF_API_VULKAN_CONTIGUOUS_MEMORY (LIBAVUTIL_VERSION_MAJOR < 59)
/**
* @}
* @}
*/
#endif /* AVUTIL_VERSION_H */

492
dreamcast/pvrtex/log.c Normal file
View File

@ -0,0 +1,492 @@
/*
* log functions
* Copyright (c) 2003 Michel Bardiaux
*
* 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
* logging functions
*/
#include "config.h"
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#if HAVE_IO_H
#include <io.h>
#endif
#include <inttypes.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "bprint.h"
#include "common.h"
#include "internal.h"
#include "log.h"
#include "thread.h"
static AVMutex mutex = AV_MUTEX_INITIALIZER;
#define LINE_SZ 1024
#if HAVE_VALGRIND_VALGRIND_H
#include <valgrind/valgrind.h>
/* this is the log level at which valgrind will output a full backtrace */
#define BACKTRACE_LOGLEVEL AV_LOG_ERROR
#endif
static int av_log_level = AV_LOG_INFO;
static int flags;
#define NB_LEVELS 8
#if defined(_WIN32) && HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE
#include <windows.h>
static const uint8_t color[16 + AV_CLASS_CATEGORY_NB] = {
[AV_LOG_PANIC /8] = 12,
[AV_LOG_FATAL /8] = 12,
[AV_LOG_ERROR /8] = 12,
[AV_LOG_WARNING/8] = 14,
[AV_LOG_INFO /8] = 7,
[AV_LOG_VERBOSE/8] = 10,
[AV_LOG_DEBUG /8] = 10,
[AV_LOG_TRACE /8] = 8,
[16+AV_CLASS_CATEGORY_NA ] = 7,
[16+AV_CLASS_CATEGORY_INPUT ] = 13,
[16+AV_CLASS_CATEGORY_OUTPUT ] = 5,
[16+AV_CLASS_CATEGORY_MUXER ] = 13,
[16+AV_CLASS_CATEGORY_DEMUXER ] = 5,
[16+AV_CLASS_CATEGORY_ENCODER ] = 11,
[16+AV_CLASS_CATEGORY_DECODER ] = 3,
[16+AV_CLASS_CATEGORY_FILTER ] = 10,
[16+AV_CLASS_CATEGORY_BITSTREAM_FILTER] = 9,
[16+AV_CLASS_CATEGORY_SWSCALER ] = 7,
[16+AV_CLASS_CATEGORY_SWRESAMPLER ] = 7,
[16+AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT ] = 13,
[16+AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT ] = 5,
[16+AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT ] = 13,
[16+AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT ] = 5,
[16+AV_CLASS_CATEGORY_DEVICE_OUTPUT ] = 13,
[16+AV_CLASS_CATEGORY_DEVICE_INPUT ] = 5,
};
static int16_t background, attr_orig;
static HANDLE con;
#else
static const uint32_t color[16 + AV_CLASS_CATEGORY_NB] = {
[AV_LOG_PANIC /8] = 52 << 16 | 196 << 8 | 0x41,
[AV_LOG_FATAL /8] = 208 << 8 | 0x41,
[AV_LOG_ERROR /8] = 196 << 8 | 0x11,
[AV_LOG_WARNING/8] = 226 << 8 | 0x03,
[AV_LOG_INFO /8] = 253 << 8 | 0x09,
[AV_LOG_VERBOSE/8] = 40 << 8 | 0x02,
[AV_LOG_DEBUG /8] = 34 << 8 | 0x02,
[AV_LOG_TRACE /8] = 34 << 8 | 0x07,
[16+AV_CLASS_CATEGORY_NA ] = 250 << 8 | 0x09,
[16+AV_CLASS_CATEGORY_INPUT ] = 219 << 8 | 0x15,
[16+AV_CLASS_CATEGORY_OUTPUT ] = 201 << 8 | 0x05,
[16+AV_CLASS_CATEGORY_MUXER ] = 213 << 8 | 0x15,
[16+AV_CLASS_CATEGORY_DEMUXER ] = 207 << 8 | 0x05,
[16+AV_CLASS_CATEGORY_ENCODER ] = 51 << 8 | 0x16,
[16+AV_CLASS_CATEGORY_DECODER ] = 39 << 8 | 0x06,
[16+AV_CLASS_CATEGORY_FILTER ] = 155 << 8 | 0x12,
[16+AV_CLASS_CATEGORY_BITSTREAM_FILTER] = 192 << 8 | 0x14,
[16+AV_CLASS_CATEGORY_SWSCALER ] = 153 << 8 | 0x14,
[16+AV_CLASS_CATEGORY_SWRESAMPLER ] = 147 << 8 | 0x14,
[16+AV_CLASS_CATEGORY_DEVICE_VIDEO_OUTPUT ] = 213 << 8 | 0x15,
[16+AV_CLASS_CATEGORY_DEVICE_VIDEO_INPUT ] = 207 << 8 | 0x05,
[16+AV_CLASS_CATEGORY_DEVICE_AUDIO_OUTPUT ] = 213 << 8 | 0x15,
[16+AV_CLASS_CATEGORY_DEVICE_AUDIO_INPUT ] = 207 << 8 | 0x05,
[16+AV_CLASS_CATEGORY_DEVICE_OUTPUT ] = 213 << 8 | 0x15,
[16+AV_CLASS_CATEGORY_DEVICE_INPUT ] = 207 << 8 | 0x05,
};
#endif
static int use_color = -1;
#if defined(_WIN32) && HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE
static void win_console_puts(const char *str)
{
const uint8_t *q = str;
uint16_t line[LINE_SZ];
while (*q) {
uint16_t *buf = line;
DWORD nb_chars = 0;
DWORD written;
while (*q && nb_chars < LINE_SZ - 1) {
uint32_t ch;
uint16_t tmp;
GET_UTF8(ch, *q ? *q++ : 0, ch = 0xfffd; goto continue_on_invalid;)
continue_on_invalid:
PUT_UTF16(ch, tmp, *buf++ = tmp; nb_chars++;)
}
WriteConsoleW(con, line, nb_chars, &written, NULL);
}
}
#endif
static void check_color_terminal(void)
{
char *term = getenv("TERM");
#if defined(_WIN32) && HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE
CONSOLE_SCREEN_BUFFER_INFO con_info;
DWORD dummy;
con = GetStdHandle(STD_ERROR_HANDLE);
if (con != INVALID_HANDLE_VALUE && !GetConsoleMode(con, &dummy))
con = INVALID_HANDLE_VALUE;
if (con != INVALID_HANDLE_VALUE) {
GetConsoleScreenBufferInfo(con, &con_info);
attr_orig = con_info.wAttributes;
background = attr_orig & 0xF0;
}
#endif
if (getenv("AV_LOG_FORCE_NOCOLOR")) {
use_color = 0;
} else if (getenv("AV_LOG_FORCE_COLOR")) {
use_color = 1;
} else {
#if defined(_WIN32) && HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE
use_color = (con != INVALID_HANDLE_VALUE);
#elif HAVE_ISATTY
use_color = (term && isatty(2));
#else
use_color = 0;
#endif
}
if (getenv("AV_LOG_FORCE_256COLOR") || (term && strstr(term, "256color")))
use_color *= 256;
}
static void ansi_fputs(int level, int tint, const char *str, int local_use_color)
{
if (local_use_color == 1) {
fprintf(stderr,
"\033[%"PRIu32";3%"PRIu32"m%s\033[0m",
(color[level] >> 4) & 15,
color[level] & 15,
str);
} else if (tint && use_color == 256) {
fprintf(stderr,
"\033[48;5;%"PRIu32"m\033[38;5;%dm%s\033[0m",
(color[level] >> 16) & 0xff,
tint,
str);
} else if (local_use_color == 256) {
fprintf(stderr,
"\033[48;5;%"PRIu32"m\033[38;5;%"PRIu32"m%s\033[0m",
(color[level] >> 16) & 0xff,
(color[level] >> 8) & 0xff,
str);
} else
fputs(str, stderr);
}
static void colored_fputs(int level, int tint, const char *str)
{
int local_use_color;
if (!*str)
return;
if (use_color < 0)
check_color_terminal();
if (level == AV_LOG_INFO/8) local_use_color = 0;
else local_use_color = use_color;
#if defined(_WIN32) && HAVE_SETCONSOLETEXTATTRIBUTE && HAVE_GETSTDHANDLE
if (con != INVALID_HANDLE_VALUE) {
if (local_use_color)
SetConsoleTextAttribute(con, background | color[level]);
win_console_puts(str);
if (local_use_color)
SetConsoleTextAttribute(con, attr_orig);
} else {
ansi_fputs(level, tint, str, local_use_color);
}
#else
ansi_fputs(level, tint, str, local_use_color);
#endif
}
const char *av_default_item_name(void *ptr)
{
return (*(AVClass **) ptr)->class_name;
}
AVClassCategory av_default_get_category(void *ptr)
{
return (*(AVClass **) ptr)->category;
}
static void sanitize(uint8_t *line){
while(*line){
if(*line < 0x08 || (*line > 0x0D && *line < 0x20))
*line='?';
line++;
}
}
static int get_category(void *ptr){
AVClass *avc = *(AVClass **) ptr;
if( !avc
|| (avc->version&0xFF)<100
|| avc->version < (51 << 16 | 59 << 8)
|| avc->category >= AV_CLASS_CATEGORY_NB) return AV_CLASS_CATEGORY_NA + 16;
if(avc->get_category)
return avc->get_category(ptr) + 16;
return avc->category + 16;
}
static const char *get_level_str(int level)
{
switch (level) {
case AV_LOG_QUIET:
return "quiet";
case AV_LOG_DEBUG:
return "debug";
case AV_LOG_TRACE:
return "trace";
case AV_LOG_VERBOSE:
return "verbose";
case AV_LOG_INFO:
return "info";
case AV_LOG_WARNING:
return "warning";
case AV_LOG_ERROR:
return "error";
case AV_LOG_FATAL:
return "fatal";
case AV_LOG_PANIC:
return "panic";
default:
return "";
}
}
static void format_line(void *avcl, int level, const char *fmt, va_list vl,
AVBPrint part[4], int *print_prefix, int type[2])
{
AVClass* avc = avcl ? *(AVClass **) avcl : NULL;
av_bprint_init(part+0, 0, AV_BPRINT_SIZE_AUTOMATIC);
av_bprint_init(part+1, 0, AV_BPRINT_SIZE_AUTOMATIC);
av_bprint_init(part+2, 0, AV_BPRINT_SIZE_AUTOMATIC);
av_bprint_init(part+3, 0, 65536);
if(type) type[0] = type[1] = AV_CLASS_CATEGORY_NA + 16;
if (*print_prefix && avc) {
if (avc->parent_log_context_offset) {
AVClass** parent = *(AVClass ***) (((uint8_t *) avcl) +
avc->parent_log_context_offset);
if (parent && *parent) {
av_bprintf(part+0, "[%s @ %p] ",
(*parent)->item_name(parent), parent);
if(type) type[0] = get_category(parent);
}
}
av_bprintf(part+1, "[%s @ %p] ",
avc->item_name(avcl), avcl);
if(type) type[1] = get_category(avcl);
}
if (*print_prefix && (level > AV_LOG_QUIET) && (flags & AV_LOG_PRINT_LEVEL))
av_bprintf(part+2, "[%s] ", get_level_str(level));
av_vbprintf(part+3, fmt, vl);
if(*part[0].str || *part[1].str || *part[2].str || *part[3].str) {
char lastc = part[3].len && part[3].len <= part[3].size ? part[3].str[part[3].len - 1] : 0;
*print_prefix = lastc == '\n' || lastc == '\r';
}
}
void av_log_format_line(void *ptr, int level, const char *fmt, va_list vl,
char *line, int line_size, int *print_prefix)
{
av_log_format_line2(ptr, level, fmt, vl, line, line_size, print_prefix);
}
int av_log_format_line2(void *ptr, int level, const char *fmt, va_list vl,
char *line, int line_size, int *print_prefix)
{
AVBPrint part[4];
int ret;
format_line(ptr, level, fmt, vl, part, print_prefix, NULL);
ret = snprintf(line, line_size, "%s%s%s%s", part[0].str, part[1].str, part[2].str, part[3].str);
av_bprint_finalize(part+3, NULL);
return ret;
}
void av_log_default_callback(void* ptr, int level, const char* fmt, va_list vl)
{
static int print_prefix = 1;
static int count;
static char prev[LINE_SZ];
AVBPrint part[4];
char line[LINE_SZ];
static int is_atty;
int type[2];
unsigned tint = 0;
if (level >= 0) {
tint = level & 0xff00;
level &= 0xff;
}
if (level > av_log_level)
return;
ff_mutex_lock(&mutex);
format_line(ptr, level, fmt, vl, part, &print_prefix, type);
snprintf(line, sizeof(line), "%s%s%s%s", part[0].str, part[1].str, part[2].str, part[3].str);
#if HAVE_ISATTY
if (!is_atty)
is_atty = isatty(2) ? 1 : -1;
#endif
if (print_prefix && (flags & AV_LOG_SKIP_REPEATED) && !strcmp(line, prev) &&
*line && line[strlen(line) - 1] != '\r'){
count++;
if (is_atty == 1)
fprintf(stderr, " Last message repeated %d times\r", count);
goto end;
}
if (count > 0) {
fprintf(stderr, " Last message repeated %d times\n", count);
count = 0;
}
strcpy(prev, line);
sanitize(part[0].str);
colored_fputs(type[0], 0, part[0].str);
sanitize(part[1].str);
colored_fputs(type[1], 0, part[1].str);
sanitize(part[2].str);
colored_fputs(av_clip(level >> 3, 0, NB_LEVELS - 1), tint >> 8, part[2].str);
sanitize(part[3].str);
colored_fputs(av_clip(level >> 3, 0, NB_LEVELS - 1), tint >> 8, part[3].str);
#if CONFIG_VALGRIND_BACKTRACE
if (level <= BACKTRACE_LOGLEVEL)
VALGRIND_PRINTF_BACKTRACE("%s", "");
#endif
end:
av_bprint_finalize(part+3, NULL);
ff_mutex_unlock(&mutex);
}
static void (*av_log_callback)(void*, int, const char*, va_list) =
av_log_default_callback;
void av_log(void* avcl, int level, const char *fmt, ...)
{
va_list vl;
va_start(vl, fmt);
av_vlog(avcl, level, fmt, vl);
va_end(vl);
}
void av_log_once(void* avcl, int initial_level, int subsequent_level, int *state, const char *fmt, ...)
{
va_list vl;
va_start(vl, fmt);
av_vlog(avcl, *state ? subsequent_level : initial_level, fmt, vl);
va_end(vl);
*state = 1;
}
void av_vlog(void* avcl, int level, const char *fmt, va_list vl)
{
AVClass* avc = avcl ? *(AVClass **) avcl : NULL;
void (*log_callback)(void*, int, const char*, va_list) = av_log_callback;
if (avc && avc->version >= (50 << 16 | 15 << 8 | 2) &&
avc->log_level_offset_offset && level >= AV_LOG_FATAL)
level += *(int *) (((uint8_t *) avcl) + avc->log_level_offset_offset);
if (log_callback)
log_callback(avcl, level, fmt, vl);
}
int av_log_get_level(void)
{
return av_log_level;
}
void av_log_set_level(int level)
{
av_log_level = level;
}
void av_log_set_flags(int arg)
{
flags = arg;
}
int av_log_get_flags(void)
{
return flags;
}
void av_log_set_callback(void (*callback)(void*, int, const char*, va_list))
{
av_log_callback = callback;
}
static void missing_feature_sample(int sample, void *avc, const char *msg,
va_list argument_list)
{
av_vlog(avc, AV_LOG_WARNING, msg, argument_list);
av_log(avc, AV_LOG_WARNING, " is not implemented. Update your FFmpeg "
"version to the newest one from Git. If the problem still "
"occurs, it means that your file has a feature which has not "
"been implemented.\n");
if (sample)
av_log(avc, AV_LOG_WARNING, "If you want to help, upload a sample "
"of this file to https://streams.videolan.org/upload/ "
"and contact the ffmpeg-devel mailing list. (ffmpeg-devel@ffmpeg.org)\n");
}
void avpriv_request_sample(void *avc, const char *msg, ...)
{
va_list argument_list;
va_start(argument_list, msg);
missing_feature_sample(1, avc, msg, argument_list);
va_end(argument_list);
}
void avpriv_report_missing_feature(void *avc, const char *msg, ...)
{
va_list argument_list;
va_start(argument_list, msg);
missing_feature_sample(0, avc, msg, argument_list);
va_end(argument_list);
}

446
dreamcast/pvrtex/main.c Normal file
View File

@ -0,0 +1,446 @@
#include <stdio.h>
#include <stddef.h>
#include <assert.h>
#include <string.h>
#include <ctype.h>
#include <stdarg.h>
#include "stb_image_write.h"
#include "pvr_texture_encoder.h"
#include "optparse.h"
#include "mycommon.h"
#include "file_pvr.h"
#include "file_tex.h"
int log_level = LOG_PROGRESS;
void pteLogLocV(unsigned level, const char *file, unsigned line, const char *fmt, va_list args) {
static const char * logtypes[] = {
[LOG_ALL] = "ALL",
[LOG_DEBUG] = "DEBUG",
[LOG_INFO] = "INFO",
[LOG_PROGRESS] = "PROGRESS",
[LOG_WARNING] = "WARNING",
[LOG_COMPLETION] = "COMPLETION",
[LOG_NONE] = "NONE"
};
if (level > log_level)
return;
if (log_level == LOG_DEBUG) {
if (level >= LOG_DEBUG)
level = LOG_DEBUG;
if (file == NULL)
file = "unk";
fprintf(stderr, "[%s, ln %i] %s: ", file, line, logtypes[level]);
}
vfprintf(stderr, fmt, args);
}
void pteLogLoc(unsigned level, const char *file, unsigned line, const char *fmt, ...) {
va_list args;
va_start(args, fmt);
pteLogLocV(level, file, line, fmt, args);
va_end(args);
}
void ErrorExitV(const char *fmt, va_list args) {
fprintf(stderr, "Error: ");
vfprintf(stderr, fmt, args);
exit(1);
}
void ErrorExit(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
ErrorExitV(fmt, args);
va_end(args);
exit(1);
}
void ErrorExitOn(int cond, const char *fmt, ...) {
if (!cond)
return;
va_list args;
va_start(args, fmt);
ErrorExitV(fmt, args);
va_end(args);
}
//https://cfengine.com/blog/2021/optional-arguments-with-getopt-long/
#define OPTARG_FIX_UP do { \
if (options.optarg == NULL && options.optind < argc && options.argv[options.optind][0] != '-') \
options.optarg = options.argv[options.optind++]; \
} while(0)
typedef struct {
const char *name;
int value;
} OptionMap;
static const OptionMap supported_pixel_formats[] = {
{"RGB565", PTE_RGB565},
{"ARGB1555", PTE_ARGB1555},
{"ARGB4444", PTE_ARGB4444},
{"YUV", PTE_YUV},
{"YUV422", PTE_YUV},
{"PAL8BPP", PTE_PALETTE_8B},
{"PAL4BPP", PTE_PALETTE_4B},
{"BUMPMAP", PTE_BUMP},
{"NORMAL", PTE_NORMAL},
{"AUTO", PTE_AUTO},
{"AUTOYUV", PTE_AUTO_YUV},
};
static const OptionMap resize_options[] = {
{"none", PTE_FIX_NONE},
{"near", PTE_FIX_NEAREST},
{"nearest", PTE_FIX_NEAREST},
{"up", PTE_FIX_UP},
{"down", PTE_FIX_DOWN},
};
static const OptionMap mip_resize_options[] = {
{"none", PTE_FIX_MIP_NONE},
{"x2", PTE_FIX_MIP_NARROW_X2},
{"x4", PTE_FIX_MIP_NARROW_X4},
{"up", PTE_FIX_MIP_MAX},
{"down", PTE_FIX_MIP_MIN},
};
static const OptionMap edge_options[] = {
{"clamp", STBIR_EDGE_CLAMP},
{"reflect", STBIR_EDGE_REFLECT},
{"wrap", STBIR_EDGE_WRAP},
{"zero", STBIR_EDGE_ZERO},
};
//Search through OptionMap for match and return it's value.
//If name is not found and invalid_msg is NULL, it returns default_value
//If name is not found and invalid_msg is not NULL, it prints invalid_msg and exits
int GetOptMap(const OptionMap *map, size_t mapsize, const char *name, int default_value, const char *invalid_msg) {
if (name == NULL) {
if (invalid_msg)
ErrorExit("%s", invalid_msg);
return default_value;
}
for(size_t i = 0; i < mapsize; i++) {
if (!strcasecmp(map[i].name, name)) {
return map[i].value;
}
}
if (invalid_msg)
ErrorExit("%s", invalid_msg);
return default_value;
}
int main(int argc, char **argv) {
PvrTexEncoder pte;
pteInit(&pte);
struct optparse_long longopts[] = {
{"help", 'h', OPTPARSE_NONE},
{"out", 'o', OPTPARSE_REQUIRED},
{"in", 'i', OPTPARSE_REQUIRED},
{"format", 'f', OPTPARSE_REQUIRED},
{"gamma", 'g', OPTPARSE_REQUIRED},
{"gamma-alpha", 'G', OPTPARSE_REQUIRED},
{"compress", 'c', OPTPARSE_OPTIONAL},
{"max-color", 'C', OPTPARSE_REQUIRED},
{"mipmap", 'm', OPTPARSE_OPTIONAL},
{"perfect-mip", 'M', OPTPARSE_OPTIONAL},
{"high-weight", 'H', OPTPARSE_REQUIRED},
{"preview", 'p', OPTPARSE_REQUIRED},
{"bilinear", 'b', OPTPARSE_NONE},
{"dither", 'd', OPTPARSE_OPTIONAL},
{"nearest", 'n', OPTPARSE_NONE},
{"verbose", 'v', OPTPARSE_NONE},
{"version", 'V', OPTPARSE_NONE},
{"no-mip-shift", 'S', OPTPARSE_NONE},
{"resize", 'r', OPTPARSE_OPTIONAL},
{"mip-resize", 'R', OPTPARSE_OPTIONAL},
{"stride", 's', OPTPARSE_NONE},
{"edge", 'e', OPTPARSE_REQUIRED},
{0}
};
#define MAX_FNAMES 11
const char *fnames[MAX_FNAMES];
unsigned fname_cnt = 0;
const char *outname = "";
const char *prevname = "";
//Parse command line parameters
struct optparse options;
int option;
optparse_init(&options, argv);
while ((option = optparse_long(&options, longopts, NULL)) != -1) {
switch(option) {
case 'h':
printf("No help yet\n");
return 0;
break;
case 'i':
ErrorExitOn(fname_cnt >= MAX_FNAMES, "Too many input files have been specified\n");
fnames[fname_cnt++] = options.optarg;
break;
case 'o':
outname = options.optarg;
break;
case 'f':
pte.pixel_format = GetOptMap(supported_pixel_formats, ARR_SIZE(supported_pixel_formats), options.optarg, -1, "invalid pixel format\n");
break;
case 'g':
if (sscanf(options.optarg, "%f", &pte.rgb_gamma) != 1)
ErrorExit("invalid gamma\n");
break;
case 'G':
if (sscanf(options.optarg, "%f", &pte.alpha_gamma) != 1)
ErrorExit("invalid alpha gamma\n");
break;
case 'r':
OPTARG_FIX_UP;
pte.resize = PTE_FIX_NEAREST;
if (options.optarg) {
pte.resize = GetOptMap(resize_options, ARR_SIZE(resize_options), options.optarg, PTE_FIX_UP, "invalid resize value\n");
}
break;
case 'R':
OPTARG_FIX_UP;
pte.mipresize = PTE_FIX_MIP_NARROW_X2;
if (options.optarg) {
pte.mipresize = GetOptMap(mip_resize_options, ARR_SIZE(mip_resize_options), options.optarg, PTE_FIX_MIP_NARROW_X2, "invalid mip resize value\n");
}
break;
case 'p':
prevname = options.optarg;
break;
case 'S':
pte.mip_shift_correction = false;
break;
case 's':
pte.stride = true;
break;
case 'e':
pte.edge_method = GetOptMap(edge_options, ARR_SIZE(edge_options), options.optarg, -0, "invalid edge handling method\n");
break;
case 'H':
if (sscanf(options.optarg, "%u", &pte.high_weight_mips) != 1) {
ErrorExit("invalid high weight parameter, must be an integer between 1 and the number of mipmap levels\n");
}
break;
case 'n':
ErrorExit("Option -%c not supported yet\n", option);
break;
case 'v':
log_level = LOG_INFO;
//If someone runs this with only -v as a parameter, they probably want the version
if (argc != 2)
break;
//Fallthrough
case 'V':
printf("pvrtex - Dreamcast Texture Encoder - Version 1.01\n");
return 0;
case 'b':
pteLog(LOG_WARNING, "Option --bilinear does nothing\n");
break;
case 'd': {
OPTARG_FIX_UP;
pte.dither = 1.0f;
if (options.optarg) {
if ((sscanf(options.optarg, "%f", &pte.dither) != 1) || (pte.dither < 0) || (pte.dither > 1)) {
ErrorExit("invalid dither amount parameter, should be in the range [0, 1]\n");
}
}
} break;
case 'c': {
OPTARG_FIX_UP;
unsigned cbsize = 256;
if (options.optarg) {
if (!strcasecmp(options.optarg, "small") || !strcasecmp(options.optarg, "sm")) {
pte.auto_small_vq = true;
} else if ((sscanf(options.optarg, "%u", &cbsize) != 1) || (cbsize <= 0) || (cbsize > 256)) {
ErrorExit("invalid compression parameter (%s)\n", options.optarg);
}
else if(cbsize > 0 && cbsize < 256) {
pte.auto_small_vq = true; // output smaller codebook!
}
}
pteSetCompressed(&pte, cbsize);
} break;
case 'm':
OPTARG_FIX_UP;
pte.want_mips = PTE_MIP_QUALITY;
if (options.optarg) {
if (!strcasecmp(options.optarg, "fast"))
pte.want_mips = PTE_MIP_FAST;
else if (!strcasecmp(options.optarg, "quality"))
; //default
else
ErrorExit("Unknown mipmap parameter (%s)\n", options.optarg);
}
break;
case 'M':
OPTARG_FIX_UP;
pte.perfect_mips = 3;
if (options.optarg) {
if (sscanf(options.optarg, "%u", &pte.perfect_mips) != 1) {
ErrorExit("bad perfect mip value\n");
}
}
break;
case 'C':
if ((sscanf(options.optarg, "%u", &pte.palette_size) != 1) || (pte.palette_size <= 1) || (pte.palette_size > 256)) {
ErrorExit("invalid max palette size parameter (should be [1, 16] for 4bpp, or [1, 256] for 8bpp)\n");
}
break;
default:
ErrorExit("%s\n", options.errmsg);
}
}
bool have_output = strlen(outname) > 0;
bool have_preview = strlen(prevname) > 0;
//Get output extension
const char *extension = "";
if (have_output) {
extension = strrchr(outname, '.');
if (extension == NULL)
extension = "";
}
ErrorExitOn(!have_output && !have_preview, "No output or preview file name specified, nothing to do\n");
ErrorExitOn(fname_cnt == 0, "No input files specified\n");
pteLog(LOG_PROGRESS, "Reading input...\n");
pteLoadFromFiles(&pte, fnames, fname_cnt);
//Check and fix up image size
pteSetSize(&pte);
if (pte.pixel_format == PTE_AUTO || pte.pixel_format == PTE_AUTO_YUV)
pteAutoSelectPixelFormat(&pte);
//Fix some stuff up for .PVR files
if (strcasecmp(extension, ".pvr") == 0) {
if (pteIsCompressed(&pte)) {
//.PVR seems to require square textures if compressed
// JP - Rectangle VQ certainly does work on real hardware
//pteMakeSquare(&pte);
if (pte.auto_small_vq == true) {
//For other sizes, we make a full size codebook texture, but don't use all the entries
if(pte.codebook_size == 256)
pte.codebook_size = fPvrSmallVQCodebookSize(pte.w + pte.h, pte.want_mips);
// JP - Rectangle VQ certainly does work on real hardware
/*
if (pte.w != pte.h) {
pteLog(LOG_WARNING, ".PVR file does not support small VQ with non-square textures, using full size codebook\n");
pte.auto_small_vq = false;
} else*/ if (pte.codebook_size < 256) {
pteLog(LOG_INFO, "Making small codebook .PVR VQ is CB size of %u\n", pte.codebook_size);
}/* else {
pteLog(LOG_WARNING, ".PVR file does not support small VQ with current size/mipmap combination, using full size codebook\n");
pte.auto_small_vq = false;
}*/
}
}
}
if (strcasecmp(extension, ".dt") == 0) {
if (pte.auto_small_vq) {
//8x8 no mips has 10 entries, 128x128 with mips has 192 entires
//Pick something in between
float small_uncomp = CalcTextureSize(8, 8, PTE_ARGB1555, 0, 0, 0);
float large_uncomp = CalcTextureSize(128, 128, PTE_ARGB1555, 1, 0, 0);
unsigned small_cbsize = 10;
unsigned large_cbsize = 192;
unsigned idxsize = CalcTextureSize(pte.w, pte.h, PTE_ARGB1555, pteHasMips(&pte), 1, 0);
float uncompsize = CalcTextureSize(pte.w, pte.h, PTE_ARGB1555, pteHasMips(&pte), 0, 0);
float ratio = (uncompsize - small_uncomp) / (large_uncomp - small_uncomp);
unsigned cbsize = lerp(ratio, small_cbsize, large_cbsize);
//If size is less than 32, add extra entries to use any padding that would result
unsigned rndamt = 32;
unsigned size = idxsize + cbsize*8;
unsigned roundupsize = (size + rndamt - 1) & ~(rndamt-1);
unsigned extraroom = roundupsize - size;
pteLog(LOG_DEBUG, "Idx %u, CBsize %u, Extra %u\n", idxsize, cbsize, extraroom);
pte.codebook_size = CLAMP(8, cbsize + extraroom/8, 256);
}
//.DT supports codebook offsets for true reduced codebooks
pte.pvr_idx_offset = PVR_FULL_CODEBOOK - pte.codebook_size;
}
//If no edge method is specified, use clamp if no using mipmaps, or wrap if we are
if (pte.edge_method == 0) {
if (pte.want_mips)
pte.edge_method = STBIR_EDGE_WRAP;
else
pte.edge_method = STBIR_EDGE_CLAMP;
}
pteEncodeTexture(&pte);
//Make preview
if (have_preview) {
const char *prevextension = strrchr(prevname, '.');
if (prevextension != NULL) {
pteLog(LOG_PROGRESS, "Writing preview to \"%s\"...\n", prevname);
pteGeneratePreviews(&pte);
//Write preview image to file
if (strcasecmp(prevextension, ".png") == 0)
stbi_write_png(prevname, pte.final_preview_w, pte.h, 4, pte.final_preview, 0);
else if (strcasecmp(prevextension, ".jpg") == 0 || strcasecmp(prevextension, ".jpeg") == 0)
stbi_write_jpg(prevname, pte.final_preview_w, pte.h, 4, pte.final_preview, 95);
else if (strcasecmp(prevextension, ".bmp") == 0)
stbi_write_bmp(prevname, pte.final_preview_w, pte.h, 4, pte.final_preview);
else if (strcasecmp(prevextension, ".tga") == 0)
stbi_write_tga(prevname, pte.final_preview_w, pte.h, 4, pte.final_preview);
else
pteLog(LOG_WARNING, "Skipping preview creation because of unknown file type (%s). Supported types are PNG, JPG, BMP, and TGA.\n", prevextension);
} else {
pteLog(LOG_WARNING, "No extension specified for preview, don't know what type to make. Supported types are PNG, JPG, BMP, and TGA.\n");
}
}
//Write resulting texture
if (have_output) {
if (strcasecmp(extension, ".pvr") == 0) {
pteLog(LOG_COMPLETION, "Writing .PVR to \"%s\"...\n", outname);
fPvrWrite(&pte, outname);
} else if (strcasecmp(extension, ".tex") == 0 || strcasecmp(extension, ".vq") == 0) {
pteLog(LOG_COMPLETION, "Writing texconv .TEX to \"%s\"...\n", outname);
fTexWrite(&pte, outname);
if (pteIsPalettized(&pte))
fTexWritePaletteAppendPal(&pte, outname);
} else if (strcasecmp(extension, ".dt") == 0) {
pteLog(LOG_COMPLETION, "Writing .DT to \"%s\"...\n", outname);
void fDtWrite(const PvrTexEncoder *pte, const char *outfname);
fDtWrite(&pte, outname);
if (pteIsPalettized(&pte))
fTexWritePaletteAppendPal(&pte, outname);
} else {
ErrorExit("Unsupported output file type: \"%s\"\n", extension);
}
} else {
pteLog(LOG_COMPLETION, "No output file specified\n");
}
pteFree(&pte);
return 0;
}

208
dreamcast/pvrtex/md5.c Normal file
View File

@ -0,0 +1,208 @@
/*
* Copyright (C) 2006 Michael Niedermayer (michaelni@gmx.at)
* Copyright (C) 2003-2005 by Christopher R. Hertel (crh@ubiqx.mn.org)
*
* References:
* IETF RFC 1321: The MD5 Message-Digest Algorithm
* Ron Rivest. IETF, April, 1992
*
* based on http://ubiqx.org/libcifs/source/Auth/MD5.c
* from Christopher R. Hertel (crh@ubiqx.mn.org)
* Simplified, cleaned and IMO redundant comments removed by Michael.
*
* If you use gcc, then version 4.1 or later and -fomit-frame-pointer is
* strongly recommended.
*
* 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
*/
#include <stdint.h>
#include "bswap.h"
#include "intreadwrite.h"
#include "mem.h"
#include "md5.h"
typedef struct AVMD5 {
uint64_t len;
uint8_t block[64];
uint32_t ABCD[4];
} AVMD5;
const int av_md5_size = sizeof(AVMD5);
struct AVMD5 *av_md5_alloc(void)
{
return av_mallocz(sizeof(struct AVMD5));
}
static const uint8_t S[4][4] = {
{ 7, 12, 17, 22 }, /* round 1 */
{ 5, 9, 14, 20 }, /* round 2 */
{ 4, 11, 16, 23 }, /* round 3 */
{ 6, 10, 15, 21 } /* round 4 */
};
static const uint32_t T[64] = { // T[i]= fabs(sin(i+1)<<32)
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, /* round 1 */
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, /* round 2 */
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, /* round 3 */
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, /* round 4 */
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
};
#define CORE(i, a, b, c, d) \
do { \
t = S[i >> 4][i & 3]; \
a += T[i]; \
\
if (i < 32) { \
if (i < 16) \
a += (d ^ (b & (c ^ d))) + AV_RL32(X+( i & 15));\
else \
a += ((d & b) | (~d & c)) + AV_RL32(X+((1 + 5*i) & 15));\
} else { \
if (i < 48) \
a += (b ^ c ^ d) + AV_RL32(X+((5 + 3*i) & 15));\
else \
a += (c ^ (b | ~d)) + AV_RL32(X+(( 7*i) & 15));\
} \
a = b + (a << t | a >> (32 - t)); \
} while (0)
static void body(uint32_t ABCD[4], const uint8_t *src, size_t nblocks)
{
const uint32_t *X;
uint32_t a, b, c, d, t;
for (size_t n = 0; n < nblocks; n++) {
a = ABCD[3];
b = ABCD[2];
c = ABCD[1];
d = ABCD[0];
X = (const uint32_t *)src + n * 16;
#if CONFIG_SMALL
for (int i = 0; i < 64; i++) {
CORE(i, a, b, c, d);
t = d;
d = c;
c = b;
b = a;
a = t;
}
#else
#define CORE2(i) \
CORE(i, a, b, c, d); CORE((i + 1), d, a, b, c); \
CORE((i + 2), c, d, a, b); CORE((i + 3), b, c, d, a)
#define CORE4(i) CORE2(i); CORE2((i + 4)); CORE2((i + 8)); CORE2((i + 12))
CORE4(0);
CORE4(16);
CORE4(32);
CORE4(48);
#endif
ABCD[0] += d;
ABCD[1] += c;
ABCD[2] += b;
ABCD[3] += a;
}
}
void av_md5_init(AVMD5 *ctx)
{
ctx->len = 0;
ctx->ABCD[0] = 0x10325476;
ctx->ABCD[1] = 0x98badcfe;
ctx->ABCD[2] = 0xefcdab89;
ctx->ABCD[3] = 0x67452301;
}
void av_md5_update(AVMD5 *ctx, const uint8_t *src, size_t len)
{
const uint8_t *end;
int j;
j = ctx->len & 63;
ctx->len += len;
if (j) {
int cnt = FFMIN(len, 64 - j);
memcpy(ctx->block + j, src, cnt);
src += cnt;
len -= cnt;
if (j + cnt < 64)
return;
body(ctx->ABCD, ctx->block, 1);
}
end = src + (len & ~63);
if (!HAVE_FAST_UNALIGNED && ((intptr_t)src & 3)) {
while (src < end) {
memcpy(ctx->block, src, 64);
body(ctx->ABCD, ctx->block, 1);
src += 64;
}
} else {
size_t nblocks = len / 64;
body(ctx->ABCD, src, nblocks);
src = end;
}
len &= 63;
if (len > 0)
memcpy(ctx->block, src, len);
}
void av_md5_final(AVMD5 *ctx, uint8_t *dst)
{
int i;
uint64_t finalcount = av_le2ne64(ctx->len << 3);
av_md5_update(ctx, "\200", 1);
while ((ctx->len & 63) != 56)
av_md5_update(ctx, "", 1);
av_md5_update(ctx, (uint8_t *) &finalcount, 8);
for (i = 0; i < 4; i++)
AV_WL32(dst + 4 * i, ctx->ABCD[3 - i]);
}
void av_md5_sum(uint8_t *dst, const uint8_t *src, size_t len)
{
AVMD5 ctx;
av_md5_init(&ctx);
av_md5_update(&ctx, src, len);
av_md5_final(&ctx, dst);
}

568
dreamcast/pvrtex/mem.c Normal file
View File

@ -0,0 +1,568 @@
/*
* default memory allocator for libavutil
* Copyright (c) 2002 Fabrice Bellard
*
* 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
* default memory allocator for libavutil
*/
#define _XOPEN_SOURCE 600
#include "config.h"
#include <limits.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdatomic.h>
#include <string.h>
#if HAVE_MALLOC_H
#include <malloc.h>
#endif
#include "attributes.h"
#include "avassert.h"
#include "dynarray.h"
#include "error.h"
#include "internal.h"
#include "intreadwrite.h"
#include "macros.h"
#include "mem.h"
#ifdef MALLOC_PREFIX
#define malloc AV_JOIN(MALLOC_PREFIX, malloc)
#define memalign AV_JOIN(MALLOC_PREFIX, memalign)
#define posix_memalign AV_JOIN(MALLOC_PREFIX, posix_memalign)
#define realloc AV_JOIN(MALLOC_PREFIX, realloc)
#define free AV_JOIN(MALLOC_PREFIX, free)
void *malloc(size_t size);
void *memalign(size_t align, size_t size);
int posix_memalign(void **ptr, size_t align, size_t size);
void *realloc(void *ptr, size_t size);
void free(void *ptr);
#endif /* MALLOC_PREFIX */
#define ALIGN (HAVE_AVX512 ? 64 : (HAVE_AVX ? 32 : 16))
/* NOTE: if you want to override these functions with your own
* implementations (not recommended) you have to link libav* as
* dynamic libraries and remove -Wl,-Bsymbolic from the linker flags.
* Note that this will cost performance. */
static atomic_size_t max_alloc_size = ATOMIC_VAR_INIT(INT_MAX);
void av_max_alloc(size_t max){
atomic_store_explicit(&max_alloc_size, max, memory_order_relaxed);
}
static int size_mult(size_t a, size_t b, size_t *r)
{
size_t t;
#if (!defined(__INTEL_COMPILER) && AV_GCC_VERSION_AT_LEAST(5,1)) || AV_HAS_BUILTIN(__builtin_mul_overflow)
if (__builtin_mul_overflow(a, b, &t))
return AVERROR(EINVAL);
#else
t = a * b;
/* Hack inspired from glibc: don't try the division if nelem and elsize
* are both less than sqrt(SIZE_MAX). */
if ((a | b) >= ((size_t)1 << (sizeof(size_t) * 4)) && a && t / a != b)
return AVERROR(EINVAL);
#endif
*r = t;
return 0;
}
void *av_malloc(size_t size)
{
void *ptr = NULL;
if (size > atomic_load_explicit(&max_alloc_size, memory_order_relaxed))
return NULL;
#if HAVE_POSIX_MEMALIGN
if (size) //OS X on SDK 10.6 has a broken posix_memalign implementation
if (posix_memalign(&ptr, ALIGN, size))
ptr = NULL;
#elif HAVE_ALIGNED_MALLOC
ptr = _aligned_malloc(size, ALIGN);
#elif HAVE_MEMALIGN
#ifndef __DJGPP__
ptr = memalign(ALIGN, size);
#else
ptr = memalign(size, ALIGN);
#endif
/* Why 64?
* Indeed, we should align it:
* on 4 for 386
* on 16 for 486
* on 32 for 586, PPro - K6-III
* on 64 for K7 (maybe for P3 too).
* Because L1 and L2 caches are aligned on those values.
* But I don't want to code such logic here!
*/
/* Why 32?
* For AVX ASM. SSE / NEON needs only 16.
* Why not larger? Because I did not see a difference in benchmarks ...
*/
/* benchmarks with P3
* memalign(64) + 1 3071, 3051, 3032
* memalign(64) + 2 3051, 3032, 3041
* memalign(64) + 4 2911, 2896, 2915
* memalign(64) + 8 2545, 2554, 2550
* memalign(64) + 16 2543, 2572, 2563
* memalign(64) + 32 2546, 2545, 2571
* memalign(64) + 64 2570, 2533, 2558
*
* BTW, malloc seems to do 8-byte alignment by default here.
*/
#else
ptr = malloc(size);
#endif
if(!ptr && !size) {
size = 1;
ptr= av_malloc(1);
}
#if CONFIG_MEMORY_POISONING
if (ptr)
memset(ptr, FF_MEMORY_POISON, size);
#endif
return ptr;
}
void *av_realloc(void *ptr, size_t size)
{
void *ret;
if (size > atomic_load_explicit(&max_alloc_size, memory_order_relaxed))
return NULL;
#if HAVE_ALIGNED_MALLOC
ret = _aligned_realloc(ptr, size + !size, ALIGN);
#else
ret = realloc(ptr, size + !size);
#endif
#if CONFIG_MEMORY_POISONING
if (ret && !ptr)
memset(ret, FF_MEMORY_POISON, size);
#endif
return ret;
}
void *av_realloc_f(void *ptr, size_t nelem, size_t elsize)
{
size_t size;
void *r;
if (size_mult(elsize, nelem, &size)) {
av_free(ptr);
return NULL;
}
r = av_realloc(ptr, size);
if (!r)
av_free(ptr);
return r;
}
int av_reallocp(void *ptr, size_t size)
{
void *val;
if (!size) {
av_freep(ptr);
return 0;
}
memcpy(&val, ptr, sizeof(val));
val = av_realloc(val, size);
if (!val) {
av_freep(ptr);
return AVERROR(ENOMEM);
}
memcpy(ptr, &val, sizeof(val));
return 0;
}
void *av_malloc_array(size_t nmemb, size_t size)
{
size_t result;
if (size_mult(nmemb, size, &result) < 0)
return NULL;
return av_malloc(result);
}
void *av_realloc_array(void *ptr, size_t nmemb, size_t size)
{
size_t result;
if (size_mult(nmemb, size, &result) < 0)
return NULL;
return av_realloc(ptr, result);
}
int av_reallocp_array(void *ptr, size_t nmemb, size_t size)
{
void *val;
memcpy(&val, ptr, sizeof(val));
val = av_realloc_f(val, nmemb, size);
memcpy(ptr, &val, sizeof(val));
if (!val && nmemb && size)
return AVERROR(ENOMEM);
return 0;
}
void av_free(void *ptr)
{
#if HAVE_ALIGNED_MALLOC
_aligned_free(ptr);
#else
free(ptr);
#endif
}
void av_freep(void *arg)
{
void *val;
memcpy(&val, arg, sizeof(val));
memcpy(arg, &(void *){ NULL }, sizeof(val));
av_free(val);
}
void *av_mallocz(size_t size)
{
void *ptr = av_malloc(size);
if (ptr)
memset(ptr, 0, size);
return ptr;
}
void *av_calloc(size_t nmemb, size_t size)
{
size_t result;
if (size_mult(nmemb, size, &result) < 0)
return NULL;
return av_mallocz(result);
}
char *av_strdup(const char *s)
{
char *ptr = NULL;
if (s) {
size_t len = strlen(s) + 1;
ptr = av_realloc(NULL, len);
if (ptr)
memcpy(ptr, s, len);
}
return ptr;
}
char *av_strndup(const char *s, size_t len)
{
char *ret = NULL, *end;
if (!s)
return NULL;
end = memchr(s, 0, len);
if (end)
len = end - s;
ret = av_realloc(NULL, len + 1);
if (!ret)
return NULL;
memcpy(ret, s, len);
ret[len] = 0;
return ret;
}
void *av_memdup(const void *p, size_t size)
{
void *ptr = NULL;
if (p) {
ptr = av_malloc(size);
if (ptr)
memcpy(ptr, p, size);
}
return ptr;
}
int av_dynarray_add_nofree(void *tab_ptr, int *nb_ptr, void *elem)
{
void **tab;
memcpy(&tab, tab_ptr, sizeof(tab));
FF_DYNARRAY_ADD(INT_MAX, sizeof(*tab), tab, *nb_ptr, {
tab[*nb_ptr] = elem;
memcpy(tab_ptr, &tab, sizeof(tab));
}, {
return AVERROR(ENOMEM);
});
return 0;
}
void av_dynarray_add(void *tab_ptr, int *nb_ptr, void *elem)
{
void **tab;
memcpy(&tab, tab_ptr, sizeof(tab));
FF_DYNARRAY_ADD(INT_MAX, sizeof(*tab), tab, *nb_ptr, {
tab[*nb_ptr] = elem;
memcpy(tab_ptr, &tab, sizeof(tab));
}, {
*nb_ptr = 0;
av_freep(tab_ptr);
});
}
void *av_dynarray2_add(void **tab_ptr, int *nb_ptr, size_t elem_size,
const uint8_t *elem_data)
{
uint8_t *tab_elem_data = NULL;
FF_DYNARRAY_ADD(INT_MAX, elem_size, *tab_ptr, *nb_ptr, {
tab_elem_data = (uint8_t *)*tab_ptr + (*nb_ptr) * elem_size;
if (elem_data)
memcpy(tab_elem_data, elem_data, elem_size);
else if (CONFIG_MEMORY_POISONING)
memset(tab_elem_data, FF_MEMORY_POISON, elem_size);
}, {
av_freep(tab_ptr);
*nb_ptr = 0;
});
return tab_elem_data;
}
static void fill16(uint8_t *dst, int len)
{
uint32_t v = AV_RN16(dst - 2);
v |= v << 16;
while (len >= 4) {
AV_WN32(dst, v);
dst += 4;
len -= 4;
}
while (len--) {
*dst = dst[-2];
dst++;
}
}
static void fill24(uint8_t *dst, int len)
{
#if HAVE_BIGENDIAN
uint32_t v = AV_RB24(dst - 3);
uint32_t a = v << 8 | v >> 16;
uint32_t b = v << 16 | v >> 8;
uint32_t c = v << 24 | v;
#else
uint32_t v = AV_RL24(dst - 3);
uint32_t a = v | v << 24;
uint32_t b = v >> 8 | v << 16;
uint32_t c = v >> 16 | v << 8;
#endif
while (len >= 12) {
AV_WN32(dst, a);
AV_WN32(dst + 4, b);
AV_WN32(dst + 8, c);
dst += 12;
len -= 12;
}
if (len >= 4) {
AV_WN32(dst, a);
dst += 4;
len -= 4;
}
if (len >= 4) {
AV_WN32(dst, b);
dst += 4;
len -= 4;
}
while (len--) {
*dst = dst[-3];
dst++;
}
}
static void fill32(uint8_t *dst, int len)
{
uint32_t v = AV_RN32(dst - 4);
#if HAVE_FAST_64BIT
uint64_t v2= v + ((uint64_t)v<<32);
while (len >= 32) {
AV_WN64(dst , v2);
AV_WN64(dst+ 8, v2);
AV_WN64(dst+16, v2);
AV_WN64(dst+24, v2);
dst += 32;
len -= 32;
}
#endif
while (len >= 4) {
AV_WN32(dst, v);
dst += 4;
len -= 4;
}
while (len--) {
*dst = dst[-4];
dst++;
}
}
void av_memcpy_backptr(uint8_t *dst, int back, int cnt)
{
const uint8_t *src = &dst[-back];
if (!back)
return;
if (back == 1) {
memset(dst, *src, cnt);
} else if (back == 2) {
fill16(dst, cnt);
} else if (back == 3) {
fill24(dst, cnt);
} else if (back == 4) {
fill32(dst, cnt);
} else {
if (cnt >= 16) {
int blocklen = back;
while (cnt > blocklen) {
memcpy(dst, src, blocklen);
dst += blocklen;
cnt -= blocklen;
blocklen <<= 1;
}
memcpy(dst, src, cnt);
return;
}
if (cnt >= 8) {
AV_COPY32U(dst, src);
AV_COPY32U(dst + 4, src + 4);
src += 8;
dst += 8;
cnt -= 8;
}
if (cnt >= 4) {
AV_COPY32U(dst, src);
src += 4;
dst += 4;
cnt -= 4;
}
if (cnt >= 2) {
AV_COPY16U(dst, src);
src += 2;
dst += 2;
cnt -= 2;
}
if (cnt)
*dst = *src;
}
}
void *av_fast_realloc(void *ptr, unsigned int *size, size_t min_size)
{
size_t max_size;
if (min_size <= *size)
return ptr;
max_size = atomic_load_explicit(&max_alloc_size, memory_order_relaxed);
/* *size is an unsigned, so the real maximum is <= UINT_MAX. */
max_size = FFMIN(max_size, UINT_MAX);
if (min_size > max_size) {
*size = 0;
return NULL;
}
min_size = FFMIN(max_size, FFMAX(min_size + min_size / 16 + 32, min_size));
ptr = av_realloc(ptr, min_size);
/* we could set this to the unmodified min_size but this is safer
* if the user lost the ptr and uses NULL now
*/
if (!ptr)
min_size = 0;
*size = min_size;
return ptr;
}
static inline void fast_malloc(void *ptr, unsigned int *size, size_t min_size, int zero_realloc)
{
size_t max_size;
void *val;
memcpy(&val, ptr, sizeof(val));
if (min_size <= *size) {
//av_assert0(val || !min_size);
return;
}
max_size = atomic_load_explicit(&max_alloc_size, memory_order_relaxed);
/* *size is an unsigned, so the real maximum is <= UINT_MAX. */
max_size = FFMIN(max_size, UINT_MAX);
if (min_size > max_size) {
av_freep(ptr);
*size = 0;
return;
}
min_size = FFMIN(max_size, FFMAX(min_size + min_size / 16 + 32, min_size));
av_freep(ptr);
val = zero_realloc ? av_mallocz(min_size) : av_malloc(min_size);
memcpy(ptr, &val, sizeof(val));
if (!val)
min_size = 0;
*size = min_size;
return;
}
void av_fast_malloc(void *ptr, unsigned int *size, size_t min_size)
{
fast_malloc(ptr, size, min_size, 0);
}
void av_fast_mallocz(void *ptr, unsigned int *size, size_t min_size)
{
fast_malloc(ptr, size, min_size, 1);
}
int av_size_mult(size_t a, size_t b, size_t *r)
{
return size_mult(a, b, r);
}

View File

@ -0,0 +1,32 @@
#include "mycommon.h"
unsigned RoundUpPow2(unsigned val) {
val--;
val |= val >> 1;
val |= val >> 2;
val |= val >> 4;
val |= val >> 8;
val |= val >> 16;
return val+1;
}
unsigned RoundDownPow2(unsigned val) {
if (IsPow2(val))
return val;
val--;
val |= val >> 1;
val |= val >> 2;
val |= val >> 4;
val |= val >> 8;
val |= val >> 16;
return (val+1)>>1;
}
int SelectNearest(int down, int val, int up) {
return (abs(val - up) < abs(val - down)) ? up : down;
}
unsigned RoundNearest(unsigned val, unsigned round) {
val = val + round/2;
val /= round;
return val * round;
}

View File

@ -0,0 +1,40 @@
#pragma once
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#define ARR_SIZE(array) (sizeof(array) / sizeof(array[0]))
static inline bool IsPow2(uint32_t val) {
return ((val - 1) & val) == 0;
}
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
#define CLAMP(small,num,big) (MAX((small),MIN((num),(big))))
#define ROUND_UP_POW2(val, pow_of_2_amt) (((val) + ((pow_of_2_amt)-1)) & ~((pow_of_2_amt)-1))
static inline float lerp(float ratio, float a, float b) {
return ratio * b + (a - ratio * a);
}
#if 1
#define SAFE_FREE(ptr) \
if (*(ptr) != NULL) { free(*(ptr)); *(ptr) = NULL; }
#define SMART_ALLOC(ptr, size) \
do { SAFE_FREE(ptr); *ptr = calloc(size, 1); } while(0)
#else
static void SafeFree(void **ptr) {
if (*ptr != NULL) {
free(*ptr);
*ptr = NULL;
}
}
#endif
extern unsigned RoundUpPow2(unsigned val);
extern unsigned RoundDownPow2(unsigned val);
unsigned RoundNearest(unsigned val, unsigned round);
int SelectNearest(int down, int val, int up);
void ErrorExit(const char *fmt, ...);

722
dreamcast/pvrtex/nvmath.h Normal file
View File

@ -0,0 +1,722 @@
#ifndef NVMATH_H
#define NVMATH_H
#include <stdint.h>
#include <math.h>
#ifdef __CPLUSPLUS
#include <string.h>
extern "C" {
#endif
typedef int32_t nvint;
typedef union v2i v2i;
typedef union v3i v3i;
typedef union v4i v4i;
typedef union v2f v2f;
typedef union v3f v3f;
typedef union v4f v4f;
union v2i {
struct {
nvint x, y;
};
nvint v[2];
};
union v3i {
struct {
nvint x, y, z;
};
nvint v[3];
v2i xy;
};
union v4i {
struct {
nvint x, y, z, w;
};
nvint v[4];
v3i xyz;
};
union v2f {
struct {
float x, y;
};
float v[2];
#ifdef __CPLUSPLUS
inline bool operator <(const v2f &b) const { return memcmp(this, &b, sizeof(b)) < 0; }
#endif
};
union v3f {
struct {
float x, y, z;
};
float v[3];
v2f xy;
#ifdef __CPLUSPLUS
inline bool operator <(const v3f &b) const { return memcmp(this, &b, sizeof(b)) < 0; }
#endif
};
union v4f {
struct {
float x, y, z, w;
};
float v[4];
v3f xyz;
v2f xy;
};
typedef v4f vqf;
//~ #define VMATH_USE_XMTRX
#ifdef VMATH_USE_XMTRX
#include "xmtrx.h"
#endif
typedef union {
struct {
float e00, e01, e02, e03;
float e10, e11, e12, e13;
float e20, e21, e22, e23;
float e30, e31, e32, e33;
};
struct {
v4f c[4];
};
float m[16];
#ifdef VMATH_USE_XMTRX
xMatrix xm;
#endif
} m4x4f;
/*
Select how to preform square roots.
On KOS, sqrt is somehow set up to always to go a library call. Need to look
into why. You can force GCC built-in sqrt handling (recommended), or use
inline asm. Built-ins are recommended since the compiler can optimize
constant values.
**GCC is getting passed fno-builtin from $KOS_CFLAGS defined in environ_base.sh**
With the right compile options, GCC can even generate FSRRA instructions.
Turn these options on for fast builtins:
-ffast-math -ffp-contract=fast -mfsrra -mfsca
*/
#define NVMATH_TYPE_BUILTIN (0)
#define NVMATH_TYPE_LIBRARY (1)
#define NVMATH_TYPE_SH4_ASM (2)
#define NVMATH_METHOD NVMATH_TYPE_BUILTIN
#define NVMATH_MIN(a, b) ((a) < (b) ? (a) : (b))
#define NVMATH_MAX(a, b) ((a) > (b) ? (a) : (b))
#define NVMATH_MIN3(a, b, c) NVMATH_MIN(NVMATH_MIN(a, b), c)
#define NVMATH_MAX3(a, b, c) NVMATH_MAX(NVMATH_MAX(a, b), c)
#if NVMATH_METHOD == NVMATH_TYPE_LIBRARY
#define NVMATH_SQRT(a) sqrt(a)
#define NVMATH_RSQRT(a) (1.0f/sqrt(a))
#define NVMATH_SIN(rad) sinf(rad)
#define NVMATH_COS(rad) cosf(rad)
#define NVMATH_SINCOS(rad, s, c) sincosf(rad, s, c)
#define NVMATH_ACOS(rad) acosf(rad)
#define NVMATH_ABS(v) fabsf(v)
#define NVMATH_ABSI(v) abs(v)
#elif NVMATH_METHOD == NVMATH_TYPE_BUILTIN
#define NVMATH_SQRT(a) __builtin_sqrt(a)
#define NVMATH_RSQRT(a) (1.0f/__builtin_sqrt(a))
#define NVMATH_SIN(rad) __builtin_sinf(rad)
#define NVMATH_COS(rad) __builtin_cosf(rad)
#define NVMATH_SINCOS(rad, s, c) __builtin_sincosf(rad, s, c)
#define NVMATH_ACOS(rad) __builtin_acosf(rad)
#define NVMATH_ABS(v) __builtin_fabsf(v)
#define NVMATH_ABSI(v) __builtin_abs(v)
#elif NVMATH_METHOD == NVMATH_TYPE_SH4_ASM
static inline float NVMFsqrt(float a) {
__asm__( "fsqrt %0\n\t"
: "=f" (a)
: "0" (a)
: );
return a;
}
static inline float NVMFsrra(float a) {
__asm__( "fsrra %0\n\t"
: "=f" (a)
: "0" (a)
: );
return a;
}
#define NVMATH_SQRT(a) NVMFsqrt(a)
#define NVMATH_RSQRT(a) NVMFsrra(a)
static inline void NVMATH_SINCOS_I(int angle, float *sine, float *cosine)
{
register float __s __asm__("fr2");
register float __c __asm__("fr3");
asm( "lds %2,fpul\n\t"
"fsca fpul,dr2\n\t"
: "=f" (__s), "=f" (__c)
: "r" (angle)
: "fpul");
*sine = __s; *cosine = __c;
}
#define NVMATH_SINCOS(rad, s, c) NVMATH_SINCOS_I((rad) * 10430.37835f, s, c)
#else
#error NVMATH_METHOD not set
#endif
/*
vqSlerp uses an NVMATH_ASIN call. This normally requires a function call.
Defineing NVMMATH_APPROX_ASIN_ACOS replaces NVMATH_ASIN and NVMATH_ACOS
with approximate functions.
*/
#define NVMMATH_APPROX_ASIN_ACOS
#ifdef NVMMATH_APPROX_ASIN_ACOS
#undef NVMATH_ASIN
#undef NVMATH_ACOS
inline float NVMATH_ASIN(float x) {
const float scale_factor = .5707963268f;
float x5 = x * x;
x5 *= x5;
x5 *= x;
return x + scale_factor*x5;
}
inline float NVMATH_ACOS(float x) {
return M_PI_2 - NVMATH_ASIN(x);
}
#endif
#define v2Init(x,y) {{x, y}}
#define v3Init(x,y,z) {{x, y, z}}
#define v4Init(x,y,z,w) {{x, y, z, w }}
#define v2Pass(vec) (vec).x, (vec).y
#define v3Pass(vec) (vec).x, (vec).y, (vec).z
#define v4Pass(vec) (vec).x, (vec).y, (vec).z, (vec).w
//~ #define mat_elem(mat,col,row) mat[row * 4 + col]
////////////////////////////////////
#define NVM_FF4I(name, paramx, paramy, paramz, paramw, opx, opy, opz, opw) \
static inline v2i v2 ## i ## name(paramx, paramy) { \
v2i d; opx; opy; return d; } \
static inline v3i v3 ## i ## name(paramx, paramy, paramz) { \
v3i d; opx; opy; opz; return d; } \
static inline v4i v4 ## i ## name(paramx, paramy, paramz, paramw) { \
v4i d; opx; opy; opz; opw; return d; }
#define NVM_FF4(name, paramx, paramy, paramz, paramw, opx, opy, opz, opw) \
static inline v2f v2 ## name(paramx, paramy) { \
v2f d; opx; opy; return d; } \
static inline v3f v3 ## name(paramx, paramy, paramz) { \
v3f d; opx; opy; opz; return d; } \
static inline v4f v4 ## name(paramx, paramy, paramz, paramw) { \
v4f d; opx; opy; opz; opw; return d; }
#define NVM_FF1I(name, param, opx, opy, opz, opw) \
static inline v2i v2 ## i ## name(param) { \
v2i d; opx; opy; return d; } \
static inline v3i v3 ## i ## name(param) { \
v3i d; opx; opy; opz; return d; } \
static inline v4i v4 ## i ## name(param) { \
v4i d; opx; opy; opz; opw; return d; }
#define NVM_FF1(name, param, opx, opy, opz, opw) \
static inline v2f v2 ## name(param) { \
v2f d; opx; opy; return d; } \
static inline v3f v3 ## name(param) { \
v3f d; opx; opy; opz; return d; } \
static inline v4f v4 ## name(param) { \
v4f d; opx; opy; opz; opw; return d; }
#define NVM_FFU4(name, opx, opy, opz, opw) \
static inline v2f v2 ## name(v2f v) { \
v2f d; opx; opy; return d; } \
static inline v3f v3 ## name(v3f v) { \
v3f d; opx; opy; opz; return d; } \
static inline v4f v4 ## name(v4f v) { \
v4f d; opx; opy; opz; opw; return d; }
#define NVM_FFB4(name, opx, opy, opz, opw) \
static inline v2i v2 ## i ## name(v2i l, v2i r) { \
v2i d; opx; opy; return d; } \
static inline v3i v3 ## i ## name(v3i l, v3i r) { \
v3i d; opx; opy; opz; return d; } \
static inline v4i v4 ## i ## name(v4i l, v4i r) { \
v4i d; opx; opy; opz; opw; return d; } \
static inline v2f v2 ## name(v2f l, v2f r) { \
v2f d; opx; opy; return d; } \
static inline v3f v3 ## name(v3f l, v3f r) { \
v3f d; opx; opy; opz; return d; } \
static inline v4f v4 ## name(v4f l, v4f r) { \
v4f d; opx; opy; opz; opw; return d; }
#define NVM_FFB4MACV(name, opx, opy, opz, opw) \
static inline v2i v2 ## i ## name(v2i l, v2i r, v2i a) { \
v2i d; nvint aa = a.x; nvint rr = r.x; opx; aa = a.y; rr = r.y; opy; return d; } \
static inline v3i v3 ## i ## name(v3i l, v3i r, v3i a) { \
v3i d; nvint aa = a.x; nvint rr = r.x; opx; aa = a.y; rr = r.y; opy; aa = a.z; rr = r.z; opz; return d; } \
static inline v4i v4 ## i ## name(v4i l, v4i r, v4i a) { \
v4i d; nvint aa = a.x; nvint rr = r.x; opx; aa = a.y; rr = r.y; opy; aa = a.z; rr = r.z; opz; aa = a.w; rr = r.w; opw; return d; } \
static inline v2f v2 ## name(v2f l, v2f r, v2f a) { \
v2f d; float aa = a.x; float rr = r.x; opx; aa = a.y; rr = r.y; opy; return d; } \
static inline v3f v3 ## name(v3f l, v3f r, v3f a) { \
v3f d; float aa = a.x; float rr = r.x; opx; aa = a.y; rr = r.y; opy; aa = a.z; rr = r.z; opz; return d; } \
static inline v4f v4 ## name(v4f l, v4f r, v4f a) { \
v4f d; float aa = a.x; float rr = r.x; opx; aa = a.y; rr = r.y; opy; aa = a.z; rr = r.z; opz; aa = a.w; rr = r.w; opw; return d; }
#define NVM_FFB4MACS(name, opx, opy, opz, opw) \
static inline v2i v2 ## i ## name(v2i l, v2i r, float a) { \
v2i d; float aa = a; nvint rr = r.x; opx; rr = r.y; opy; return d; } \
static inline v3i v3 ## i ## name(v3i l, v3i r, float a) { \
v3i d; float aa = a; nvint rr = r.x; opx; rr = r.y; opy; rr = r.z; opz; return d; } \
static inline v4i v4 ## i ## name(v4i l, v4i r, float a) { \
v4i d; float aa = a; nvint rr = r.x; opx; rr = r.y; opy; rr = r.z; opz; rr = r.w; opw; return d; } \
static inline v2f v2 ## name(v2f l, v2f r, float a) { \
v2f d; float aa = a; float rr = r.x; opx; rr = r.y; opy; return d; } \
static inline v3f v3 ## name(v3f l, v3f r, float a) { \
v3f d; float aa = a; float rr = r.x; opx; rr = r.y; opy; rr = r.z; opz; return d; } \
static inline v4f v4 ## name(v4f l, v4f r, float a) { \
v4f d; float aa = a; float rr = r.x; opx; rr = r.y; opy; rr = r.z; opz; rr = r.w; opw; return d; }
#define NVM_FFB4MACSX(name, opx, opy, opz, opw) \
static inline v2i v2 ## i ## name(v2i l, float r, v2i a) { \
v2i d; float aa = a.x; nvint rr = r; opx; aa = a.y; opy; return d; } \
static inline v3i v3 ## i ## name(v3i l, float r, v3i a) { \
v3i d; float aa = a.x; nvint rr = r; opx; aa = a.y; opy; aa = a.z; opz; return d; } \
static inline v2f v2 ## name(v2f l, float r, v2f a) { \
v2f d; float aa = a.x; float rr = r; opx; aa = a.y; opy; return d; } \
static inline v3f v3 ## name(v3f l, float r, v3f a) { \
v3f d; float aa = a.x; float rr = r; opx; aa = a.y; opy; aa = a.z; opz; return d; } \
static inline v4f v4 ## name(v4f l, float r, v4f a) { \
v4f d; float aa = a.x; float rr = r; opx; aa = a.y; opy; aa = a.z; opz; aa = a.w; opw; return d; }
#define NVM_FFB4MAC(name, opx, opy, opz, opw) \
NVM_FFB4MACV(name, opx, opy, opz, opw) \
NVM_FFB4MACSX(name ## V, opx, opy, opz, opw) \
NVM_FFB4MACS(name ## S, opx, opy, opz, opw)
#define NVM_FFB4_COMB(name, opx, opy, opz, opw) \
static inline float v2 ## name(v2f l, v2f r) { \
return opx opy; } \
static inline float v3 ## name(v3f l, v3f r) { \
return opx opy opz; } \
static inline float v4 ## name(v4f l, v4f r) { \
return opx opy opz opw; }
#define NVM_FFB4_COMB_I(name, opx, opy, opz, opw) \
static inline float v2 ## name(v2f l, v2f r) { \
return opx opy; } \
static inline float v3 ## name(v3f l, v3f r) { \
return opx opy opz; } \
static inline float v4 ## name(v4f l, v4f r) { \
return opx opy opz opw; }
#define NVM_FFB4S(name, opx, opy, opz, opw) \
static inline v2i v2 ## i ## name(v2i l, nvint r) { \
v2i d; opx; opy; return d; } \
static inline v3i v3 ## i ## name(v3i l, nvint r) { \
v3i d; opx; opy; opz; return d; } \
static inline v4i v4 ## i ## name(v4i l, nvint r) { \
v4i d; opx; opy; opz; opw; return d; } \
static inline v2f v2 ## name(v2f l, float r) { \
v2f d; opx; opy; return d; } \
static inline v3f v3 ## name(v3f l, float r) { \
v3f d; opx; opy; opz; return d; } \
static inline v4f v4 ## name(v4f l, float r) { \
v4f d; opx; opy; opz; opw; return d; }
#define NVM_FFB4RS(name, opx, opy, opz, opw) \
static inline v2i v2 ## i ## name ## RS(nvint l, v2i r) { \
v2i d; opx; opy; return d; } \
static inline v3i v3 ## i ## name(nvint l, v3i r) { \
v3i d; opx; opy; opz; return d; } \
static inline v2f v2 ## name ## RS(float l, v2f r) { \
v2f d; opx; opy; return d; } \
static inline v3f v3 ## name(float l, v3f r) { \
v3f d; opx; opy; opz; return d; } \
static inline v4f v4 ## name(float l, v4f r) { \
v4f d; opx; opy; opz; opw; return d; }
//Binary op, associative
#define NVM_FFB4B(name, op) \
NVM_FFB4(name, \
d.x = l.x op r.x, \
d.y = l.y op r.y, \
d.z = l.z op r.z, \
d.w = l.w op r.w) \
NVM_FFB4S(name ## S, \
d.x = l.x op r, \
d.y = l.y op r, \
d.z = l.z op r, \
d.w = l.w op r)
//Binary op, not associative
#define NVM_FFB4BR(name, op) \
NVM_FFB4B(name, op) \
NVM_FFB4RS(name ## RS, \
d.x = l op r.x, \
d.y = l op r.y, \
d.z = l op r.z, \
d.w = l op r.w)
//***OPERATIONS***
/*
v?Set(float, float, float, float)
Sets vector
v?SetR(float)
Sets vector with value Repeated into all dimensions
v?Get(float*)
Creates vector from array of floats
v?Abs(v?f v)
|v|
v?Negate(v?f v)
-v
v?Recip(v?f v)
1.0f / v
v?(Add/Sub/Mul/Div)(v?f l , v?f r)
l + r
l - r
l * r
l / r
Adds, subtracts, multiplies or divides two vectors.
v?(Add/Sub/Mul/Div)S(v?f l, float r)
l + r
l - r
l * r
l / r
Adds, subtracts, multiplies or divides a left vector and a right scalar
v?(Sub/Div)SR(float l, v?f r)
l - r
l / r
Subtracts or divides a left scalar and a right vector
v?Mac(v?f l, v?f r, v?f accum)
l * r + accum
v?MacV(v?f l, float r, v?f accum)
l * r + accum
v?MacS(v?f l, v?f r, float accum)
l * r + accum
v?Mdc(v?f l, v?f r, v?f accum)
l * r - accum
v?MdcV(v?f l, float r, v?f accum)
l * r - accum
v?MdcS(v?f l, v?f r, float accum)
l * r - accum
v?Nms(v?f l, v?f r, v?f accum)
accum - l * r
v?NmsV(v?f l, float r, v?f accum)
accum - l * r
v?NmsS(v?f l, v?f r, float accum)
accum - l * r
v?Lerp(v?f start, v?f end, v?f weight)
start + weight * (end - start)
v?LerpS(v?f start, v?f end, float weight)
start + weight * (end - start)
v?Min(v?f a, v?f b)
min(a, b)
v?Max(v?f a, v?f b)
max(a, b)
v?MaxE(v?f a)
max(a.x, a.y, a.z, a.w)
v?Length(v?f v)
length of v
v?SqrLength(v?f v)
squared length of v
v?Distance(v?f a, v3f b)
distance between a and b
v?SqrDistance(v?f a, v3f b)
square distance between a and b
v?Normalize(v?f v)
v with unit length (Will divide by zero if vector is of length 0)
v?NormalizeS(v?f v)
v with unit length (returns zero vector if length zero)
v?Dot(v?f l, v?f r)
l dot r
v2Cross(v2f l, v2f r)
l cross r
v3Cross(v3f l, v3f r)
l cross r
v3Triple(v3f a, v3f b, v3f c)
a dot (b cross c)
*/
NVM_FF4I(Set, nvint x, nvint y, nvint z, nvint w,
d.x = x, d.y = y, d.z = z, d.w = w)
NVM_FF4(Set, float x, float y, float z, float w,
d.x = x, d.y = y, d.z = z, d.w = w)
NVM_FF1I(Get, const nvint *f,
d.x = f[0], d.y = f[1], d.z = f[2], d.w = f[3])
NVM_FF1(Get, const float *f,
d.x = f[0], d.y = f[1], d.z = f[2], d.w = f[3])
NVM_FF1I(SetR, int v,
d.x = v, d.y = v, d.z = v, d.w = v)
NVM_FF1(SetR, float v,
d.x = v, d.y = v, d.z = v, d.w = v)
static inline v3f v2Extv3(v2f v, float z) {
return v3Set(v.x, v.y, z);
}
static inline v4f v2Extv4(v2f v, float z, float w) {
return v4Set(v.x, v.y, z, w);
}
static inline v4f v3Extv4(v3f v, float w) {
return v4Set(v.x, v.y, v.z, w);
}
#define v2Zero() v2Set(0, 0)
#define v3Zero() v3Set(0, 0, 0)
#define v4Zero() v4Set(0, 0, 0, 0)
NVM_FFU4(Abs,
d.x = NVMATH_ABS(v.x), d.y = NVMATH_ABS(v.y), d.z = NVMATH_ABS(v.z), d.w = NVMATH_ABS(v.w))
NVM_FFU4(Negate,
d.x = -v.x, d.y = -v.y, d.z = -v.z, d.w = -v.w)
NVM_FFU4(Recip,
d.x = 1.0f/v.x, d.y = 1.0f/v.y, d.z = 1.0f/v.z, d.w = 1.0f/v.w)
NVM_FFB4B(Add, +)
NVM_FFB4BR(Sub, -)
NVM_FFB4B(Mul, *)
NVM_FFB4BR(Div, /)
NVM_FFB4_COMB(Dot,
l.x * r.x, + l.y * r.y, + l.z * r.z, + l.w * r.w)
NVM_FFB4MAC(Mac,
d.x = l.x * rr + aa,
d.y = l.y * rr + aa,
d.z = l.z * rr + aa,
d.w = l.w * rr + aa)
NVM_FFB4MAC(Mdc,
d.x = l.x * rr - aa,
d.y = l.y * rr - aa,
d.z = l.z * rr - aa,
d.w = l.w * rr - aa)
NVM_FFB4MAC(Nms,
d.x = aa - l.x * rr,
d.y = aa - l.y * rr,
d.z = aa - l.z * rr,
d.w = aa - l.w * rr)
NVM_FFB4MAC(Lerp,
d.x = l.x + aa * (rr - l.x),
d.y = l.y + aa * (rr - l.y),
d.z = l.z + aa * (rr - l.z),
d.w = l.w + aa * (rr - l.w))
NVM_FFB4(Min,
d.x = l.x < r.x ? l.x : r.x,
d.y = l.y < r.y ? l.y : r.y,
d.z = l.z < r.z ? l.z : r.z,
d.w = l.w < r.w ? l.w : r.w)
NVM_FFB4(Max,
d.x = l.x > r.x ? l.x : r.x,
d.y = l.y > r.y ? l.y : r.y,
d.z = l.z > r.z ? l.z : r.z,
d.w = l.w > r.w ? l.w : r.w)
NVM_FFB4S(MinS,
d.x = l.x < r ? l.x : r,
d.y = l.y < r ? l.y : r,
d.z = l.z < r ? l.z : r,
d.w = l.w < r ? l.w : r)
NVM_FFB4S(MaxS,
d.x = l.x > r ? l.x : r,
d.y = l.y > r ? l.y : r,
d.z = l.z > r ? l.z : r,
d.w = l.w > r ? l.w : r)
static inline float v2SqrLength(v2f v) {
return v2Dot(v, v);
}
static inline float v3SqrLength(v3f v) {
return v3Dot(v, v);
}
static inline float v4SqrLength(v4f v) {
return v4Dot(v, v);
}
static inline nvint v2iMinE(v2i v) {
return NVMATH_MIN(v.x, v.y);
}
static inline nvint v3iMinE(v3i v) {
return NVMATH_MIN(v.x, NVMATH_MIN(v.y, v.z));
}
static inline float v2MinE(v2f v) {
return NVMATH_MIN(v.x, v.y);
}
static inline float v3MinE(v3f v) {
return NVMATH_MIN(v.x, NVMATH_MIN(v.y, v.z));
}
static inline float v4MinE(v4f v) {
return NVMATH_MIN(v.x, NVMATH_MIN(v.y, NVMATH_MIN(v.z, v.w)));
}
static inline nvint v2iMaxE(v2i v) {
return NVMATH_MAX(v.x, v.y);
}
static inline nvint v3iMaxE(v3i v) {
return NVMATH_MAX(v.x, NVMATH_MAX(v.y, v.z));
}
static inline float v2MaxE(v2f v) {
return NVMATH_MAX(v.x, v.y);
}
static inline float v3MaxE(v3f v) {
return NVMATH_MAX(v.x, NVMATH_MAX(v.y, v.z));
}
static inline float v4MaxE(v4f v) {
return NVMATH_MAX(v.x, NVMATH_MAX(v.y, NVMATH_MAX(v.z, v.w)));
}
static inline float v2Sum(v2f v) {
return v2Dot(v,v2SetR(1));
}
static inline float v3Sum(v3f v) {
return v3Dot(v,v3SetR(1));
}
static inline float v4Sum(v4f v) {
return v4Dot(v,v4SetR(1));
}
static inline float v2Length(v2f v) {
return NVMATH_SQRT(v2Dot(v,v));
}
static inline float v3Length(v3f v) {
return NVMATH_SQRT(v3Dot(v,v));
}
static inline float v4Length(v4f v) {
return NVMATH_SQRT(v4Dot(v,v));
}
static inline float v2Distance(v2f a, v2f b) {
v2f v = v2Sub(a, b);
return NVMATH_SQRT(v2Dot(v,v));
}
static inline float v3Distance(v3f a, v3f b) {
v3f v = v3Sub(a, b);
return NVMATH_SQRT(v3Dot(v,v));
}
static inline float v4Distance(v4f a, v4f b) {
v4f v = v4Sub(a, b);
return NVMATH_SQRT(v4Dot(v,v));
}
static inline float v2SqrDistance(v2f a, v2f b) {
v2f v = v2Sub(a, b);
return v2Dot(v,v);
}
static inline float v3SqrDistance(v3f a, v3f b) {
v3f v = v3Sub(a, b);
return v3Dot(v,v);
}
static inline float v4SqrDistance(v4f a, v4f b) {
v4f v = v4Sub(a, b);
return v4Dot(v,v);
}
static inline v3f v3Normalize(v3f v) {
return v3MulS(v, NVMATH_RSQRT(v3Dot(v,v)));
}
static inline v4f v4Normalize(v4f v) {
return v4MulS(v, NVMATH_RSQRT(v4Dot(v,v)));
}
static inline v2f v2NormalizeS(v2f v) {
float rs = NVMATH_RSQRT(v2Dot(v,v));
return rs ? v2MulS(v, rs) : v;
}
static inline v3f v3NormalizeS(v3f v) {
float rs = NVMATH_RSQRT(v3Dot(v,v));
return rs ? v3MulS(v, rs) : v;
}
static inline v4f v4Float(v4i v) {
v4f r = { {v4Pass(v)} };
return r;
}
static inline v4i v4Int(v4f v) {
v4i r = { {v4Pass(v)} };
return r;
}
static inline v4i v4IntRnd(v4f v) {
v4i r = { {v4Pass(v4AddS(v, 0.5))} };
return r;
}
static inline float v2Cross(v2f l, v2f r) {
return l.x*r.y - l.y*r.x;
}
static inline v3f v3Cross(v3f l, v3f r) {
v3f d;
d.x = l.y*r.z - l.z*r.y;
d.y = l.z*r.x - l.x*r.z;
d.z = l.x*r.y - l.y*r.x;
return d;
}
static inline float v3Triple(v3f a, v3f b, v3f c) {
return v3Dot(a, v3Cross(b, c));
}
#ifndef VMATH_USE_XMTRX
static inline float m44Mul4Row(m4x4f m, v4f v, int row) {
return v.x * m.c[0].v[row] + v.y * m.c[1].v[row] + v.z * m.c[2].v[row] + v.w * m.c[3].v[row];
}
static inline v4f v4MulMat(m4x4f *m, v4f v) {
v4f r;
r.x = m44Mul4Row(*m, v, 0); r.y = m44Mul4Row(*m, v, 1); r.z = m44Mul4Row(*m, v, 2); r.w = m44Mul4Row(*m, v, 3);
return r;
}
#else
static inline v4f v4MulMat(m4x4f *m, v4f v) {
xmtrxLoad((xMatrix*)m);
register float x __asm__("fr0") = v.x;
register float y __asm__("fr1") = v.y;
register float z __asm__("fr2") = v.z;
register float w __asm__("fr3") = v.w;
__asm__ __volatile__(
"ftrv xmtrx,fv0\n"
: "=f" (x), "=f" (y), "=f" (z), "=f" (w)
: "0" (x), "1" (y), "2" (z), "3" (w) );
return v4Set(x, y, z, w);
}
static inline void m44Mul4x4(m4x4f * restrict l, m4x4f * restrict r, m4x4f * restrict d) {
xmtrxLoadMultiply((xMatrix*)l, (xMatrix*)r);
xmtrxStore((xMatrix*)d);
}
static inline void m44Identity(m4x4f * m) {
xmtrxIdentity(d);
xmtrxStore(d);
}
#endif
#ifdef __CPLUSPLUS
}
#endif
#endif

403
dreamcast/pvrtex/optparse.h Normal file
View File

@ -0,0 +1,403 @@
/* Optparse --- portable, reentrant, embeddable, getopt-like option parser
*
* This is free and unencumbered software released into the public domain.
*
* To get the implementation, define OPTPARSE_IMPLEMENTATION.
* Optionally define OPTPARSE_API to control the API's visibility
* and/or linkage (static, __attribute__, __declspec).
*
* The POSIX getopt() option parser has three fatal flaws. These flaws
* are solved by Optparse.
*
* 1) Parser state is stored entirely in global variables, some of
* which are static and inaccessible. This means only one thread can
* use getopt(). It also means it's not possible to recursively parse
* nested sub-arguments while in the middle of argument parsing.
* Optparse fixes this by storing all state on a local struct.
*
* 2) The POSIX standard provides no way to properly reset the parser.
* This means for portable code that getopt() is only good for one
* run, over one argv with one option string. It also means subcommand
* options cannot be processed with getopt(). Most implementations
* provide a method to reset the parser, but it's not portable.
* Optparse provides an optparse_arg() function for stepping over
* subcommands and continuing parsing of options with another option
* string. The Optparse struct itself can be passed around to
* subcommand handlers for additional subcommand option parsing. A
* full reset can be achieved by with an additional optparse_init().
*
* 3) Error messages are printed to stderr. This can be disabled with
* opterr, but the messages themselves are still inaccessible.
* Optparse solves this by writing an error message in its errmsg
* field. The downside to Optparse is that this error message will
* always be in English rather than the current locale.
*
* Optparse should be familiar with anyone accustomed to getopt(), and
* it could be a nearly drop-in replacement. The option string is the
* same and the fields have the same names as the getopt() global
* variables (optarg, optind, optopt).
*
* Optparse also supports GNU-style long options with optparse_long().
* The interface is slightly different and simpler than getopt_long().
*
* By default, argv is permuted as it is parsed, moving non-option
* arguments to the end. This can be disabled by setting the `permute`
* field to 0 after initialization.
*/
#ifndef OPTPARSE_H
#define OPTPARSE_H
#ifndef OPTPARSE_API
# define OPTPARSE_API
#endif
struct optparse {
char **argv;
int permute;
int optind;
int optopt;
char *optarg;
char errmsg[64];
int subopt;
};
enum optparse_argtype {
OPTPARSE_NONE,
OPTPARSE_REQUIRED,
OPTPARSE_OPTIONAL
};
struct optparse_long {
const char *longname;
int shortname;
enum optparse_argtype argtype;
};
/**
* Initializes the parser state.
*/
OPTPARSE_API
void optparse_init(struct optparse *options, char **argv);
/**
* Read the next option in the argv array.
* @param optstring a getopt()-formatted option string.
* @return the next option character, -1 for done, or '?' for error
*
* Just like getopt(), a character followed by no colons means no
* argument. One colon means the option has a required argument. Two
* colons means the option takes an optional argument.
*/
OPTPARSE_API
int optparse(struct optparse *options, const char *optstring);
/**
* Handles GNU-style long options in addition to getopt() options.
* This works a lot like GNU's getopt_long(). The last option in
* longopts must be all zeros, marking the end of the array. The
* longindex argument may be NULL.
*/
OPTPARSE_API
int optparse_long(struct optparse *options,
const struct optparse_long *longopts,
int *longindex);
/**
* Used for stepping over non-option arguments.
* @return the next non-option argument, or NULL for no more arguments
*
* Argument parsing can continue with optparse() after using this
* function. That would be used to parse the options for the
* subcommand returned by optparse_arg(). This function allows you to
* ignore the value of optind.
*/
OPTPARSE_API
char *optparse_arg(struct optparse *options);
/* Implementation */
#ifdef OPTPARSE_IMPLEMENTATION
#define OPTPARSE_MSG_INVALID "invalid option"
#define OPTPARSE_MSG_MISSING "option requires an argument"
#define OPTPARSE_MSG_TOOMANY "option takes no arguments"
static int
optparse_error(struct optparse *options, const char *msg, const char *data)
{
unsigned p = 0;
const char *sep = " -- '";
while (*msg)
options->errmsg[p++] = *msg++;
while (*sep)
options->errmsg[p++] = *sep++;
while (p < sizeof(options->errmsg) - 2 && *data)
options->errmsg[p++] = *data++;
options->errmsg[p++] = '\'';
options->errmsg[p++] = '\0';
return '?';
}
OPTPARSE_API
void
optparse_init(struct optparse *options, char **argv)
{
options->argv = argv;
options->permute = 1;
options->optind = argv[0] != 0;
options->subopt = 0;
options->optarg = 0;
options->errmsg[0] = '\0';
}
static int
optparse_is_dashdash(const char *arg)
{
return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] == '\0';
}
static int
optparse_is_shortopt(const char *arg)
{
return arg != 0 && arg[0] == '-' && arg[1] != '-' && arg[1] != '\0';
}
static int
optparse_is_longopt(const char *arg)
{
return arg != 0 && arg[0] == '-' && arg[1] == '-' && arg[2] != '\0';
}
static void
optparse_permute(struct optparse *options, int index)
{
char *nonoption = options->argv[index];
int i;
for (i = index; i < options->optind - 1; i++)
options->argv[i] = options->argv[i + 1];
options->argv[options->optind - 1] = nonoption;
}
static int
optparse_argtype(const char *optstring, char c)
{
int count = OPTPARSE_NONE;
if (c == ':')
return -1;
for (; *optstring && c != *optstring; optstring++);
if (!*optstring)
return -1;
if (optstring[1] == ':')
count += optstring[2] == ':' ? 2 : 1;
return count;
}
OPTPARSE_API
int
optparse(struct optparse *options, const char *optstring)
{
int type;
char *next;
char *option = options->argv[options->optind];
options->errmsg[0] = '\0';
options->optopt = 0;
options->optarg = 0;
if (option == 0) {
return -1;
} else if (optparse_is_dashdash(option)) {
options->optind++; /* consume "--" */
return -1;
} else if (!optparse_is_shortopt(option)) {
if (options->permute) {
int index = options->optind++;
int r = optparse(options, optstring);
optparse_permute(options, index);
options->optind--;
return r;
} else {
return -1;
}
}
option += options->subopt + 1;
options->optopt = option[0];
type = optparse_argtype(optstring, option[0]);
next = options->argv[options->optind + 1];
switch (type) {
case -1: {
char str[2] = {0, 0};
str[0] = option[0];
options->optind++;
return optparse_error(options, OPTPARSE_MSG_INVALID, str);
}
case OPTPARSE_NONE:
if (option[1]) {
options->subopt++;
} else {
options->subopt = 0;
options->optind++;
}
return option[0];
case OPTPARSE_REQUIRED:
options->subopt = 0;
options->optind++;
if (option[1]) {
options->optarg = option + 1;
} else if (next != 0) {
options->optarg = next;
options->optind++;
} else {
char str[2] = {0, 0};
str[0] = option[0];
options->optarg = 0;
return optparse_error(options, OPTPARSE_MSG_MISSING, str);
}
return option[0];
case OPTPARSE_OPTIONAL:
options->subopt = 0;
options->optind++;
if (option[1])
options->optarg = option + 1;
else
options->optarg = 0;
return option[0];
}
return 0;
}
OPTPARSE_API
char *
optparse_arg(struct optparse *options)
{
char *option = options->argv[options->optind];
options->subopt = 0;
if (option != 0)
options->optind++;
return option;
}
static int
optparse_longopts_end(const struct optparse_long *longopts, int i)
{
return !longopts[i].longname && !longopts[i].shortname;
}
static void
optparse_from_long(const struct optparse_long *longopts, char *optstring)
{
char *p = optstring;
int i;
for (i = 0; !optparse_longopts_end(longopts, i); i++) {
if (longopts[i].shortname && longopts[i].shortname < 127) {
int a;
*p++ = longopts[i].shortname;
for (a = 0; a < (int)longopts[i].argtype; a++)
*p++ = ':';
}
}
*p = '\0';
}
/* Unlike strcmp(), handles options containing "=". */
static int
optparse_longopts_match(const char *longname, const char *option)
{
const char *a = option, *n = longname;
if (longname == 0)
return 0;
for (; *a && *n && *a != '='; a++, n++)
if (*a != *n)
return 0;
return *n == '\0' && (*a == '\0' || *a == '=');
}
/* Return the part after "=", or NULL. */
static char *
optparse_longopts_arg(char *option)
{
for (; *option && *option != '='; option++);
if (*option == '=')
return option + 1;
else
return 0;
}
static int
optparse_long_fallback(struct optparse *options,
const struct optparse_long *longopts,
int *longindex)
{
int result;
char optstring[96 * 3 + 1]; /* 96 ASCII printable characters */
optparse_from_long(longopts, optstring);
result = optparse(options, optstring);
if (longindex != 0) {
*longindex = -1;
if (result != -1) {
int i;
for (i = 0; !optparse_longopts_end(longopts, i); i++)
if (longopts[i].shortname == options->optopt)
*longindex = i;
}
}
return result;
}
OPTPARSE_API
int
optparse_long(struct optparse *options,
const struct optparse_long *longopts,
int *longindex)
{
int i;
char *option = options->argv[options->optind];
if (option == 0) {
return -1;
} else if (optparse_is_dashdash(option)) {
options->optind++; /* consume "--" */
return -1;
} else if (optparse_is_shortopt(option)) {
return optparse_long_fallback(options, longopts, longindex);
} else if (!optparse_is_longopt(option)) {
if (options->permute) {
int index = options->optind++;
int r = optparse_long(options, longopts, longindex);
optparse_permute(options, index);
options->optind--;
return r;
} else {
return -1;
}
}
/* Parse as long option. */
options->errmsg[0] = '\0';
options->optopt = 0;
options->optarg = 0;
option += 2; /* skip "--" */
options->optind++;
for (i = 0; !optparse_longopts_end(longopts, i); i++) {
const char *name = longopts[i].longname;
if (optparse_longopts_match(name, option)) {
char *arg;
if (longindex)
*longindex = i;
options->optopt = longopts[i].shortname;
arg = optparse_longopts_arg(option);
if (longopts[i].argtype == OPTPARSE_NONE && arg != 0) {
return optparse_error(options, OPTPARSE_MSG_TOOMANY, name);
} if (arg != 0) {
options->optarg = arg;
} else if (longopts[i].argtype == OPTPARSE_REQUIRED) {
options->optarg = options->argv[options->optind];
if (options->optarg == 0)
return optparse_error(options, OPTPARSE_MSG_MISSING, name);
else
options->optind++;
}
return options->optopt;
}
}
return optparse_error(options, OPTPARSE_MSG_INVALID, option);
}
#endif /* OPTPARSE_IMPLEMENTATION */
#endif /* OPTPARSE_H */

View File

@ -0,0 +1,2 @@
#define OPTPARSE_IMPLEMENTATION
#include "optparse.h"

492
dreamcast/pvrtex/pixel.h Normal file
View File

@ -0,0 +1,492 @@
#pragma once
#include <stdint.h>
#include <assert.h>
#include "mycommon.h"
#include "nvmath.h"
typedef union {
int rgba[4];
struct {
int r, g, b, a;
};
v4i v;
} pxlRGBA32;
typedef union {
//Assumes little endian
uint32_t argb;
struct {
uint8_t b, g, r, a;
};
} pxlARGB8888;
typedef union {
//Assumes little endian
uint32_t rgba;
struct {
uint8_t a, b, g, r;
};
} pxlRGBA8888;
typedef union {
//Assumes little endian
uint32_t abgr;
struct {
uint8_t r,g,b,a;
};
} pxlABGR8888;
typedef struct {
uint8_t r, g, b;
} pxlRGB888;
typedef union {
uint16_t v;
struct {
uint16_t b : 5;
uint16_t g : 6;
uint16_t r : 5;
};
} pxlRGB565;
typedef union {
uint16_t v;
struct {
uint16_t b : 5;
uint16_t g : 5;
uint16_t r : 5;
uint16_t a : 1;
};
} pxlARGB1555;
typedef union {
uint16_t v;
struct{
uint16_t b : 4;
uint16_t g : 4;
uint16_t r : 4;
uint16_t a : 4;
};
} pxlARGB4444;
typedef union {
pxlRGB565 rgb565;
pxlARGB1555 argb1555;
pxlARGB4444 argb4444;
} pxlColor16;
typedef union {
pxlRGB888 rgb888;
} pxlColor24;
typedef union {
pxlARGB8888 argb;
pxlRGBA8888 rgba;
} pxlColor32;
static inline float pxlSatF(float val) {
return MIN(MAX(val, 0.0f), 1.0f);
}
static inline unsigned int pxlReduceRnd(unsigned int val, unsigned int rshift) {
unsigned rndup = rshift > 1 ? (1 << rshift)-1 : 0;
rndup >>= 1;
rndup = 0;
return MIN(val + rndup, 255) >> rshift;
}
static inline unsigned int pxlExpand(unsigned int val, unsigned int srcwidth) {
assert(srcwidth == 1 || srcwidth >= 4);
assert(srcwidth <= 8);
if (srcwidth == 1)
return val ? 0xff : 0;
//~ return val << (8 - srcwidth);
//~ if (srcwidth == 1)
//~ return val ? 0xff : 0;
//~ else
unsigned v = (val << (8 - srcwidth));
return v | (v >> srcwidth);
}
static inline float pxlU8toF(unsigned val) {
//Maps 0-255 to [0.0f, 1.0f]
return MIN((int)val, 255) / 255.0f;
}
static inline float pxlU8BtoF(unsigned val) {
//Maps 1-255 to [-1.0f, 1.0f]
//128 maps to 0.0
return (MIN((int)val, 255) - 128) / 127.0f;
}
static inline unsigned pxlFtoU8B(float val) {
//Maps [-1.0f, 1.0f] to [1, 255]
//0.0f maps to 128
return (CLAMP(-1, val, 1) * 127.0f) + 128.0f;
}
static inline unsigned pxlFloattoSpherical(float fx, float fy, float fz) {
v3f norm = v3Set(fx,fy,fz);
norm = v3NormalizeS(norm);
float azimuth = 0xff;
azimuth = atan2(norm.y, norm.x);
float altitude = acosf(norm.z);
const float rnd = 0.5;
int fixed_azimuth = (uint8_t)(azimuth / (2*M_PI) * 255 + rnd);
int fixed_altitude = (uint8_t)(altitude / M_PI * 255 + rnd) ^ 0xff;
return (fixed_altitude << 8) | fixed_azimuth;
}
static inline unsigned pxlRGBtoSpherical(unsigned x, unsigned y, unsigned z) {
float fx = pxlU8BtoF(x);
float fy = pxlU8BtoF(y);
float fz = pxlU8BtoF(z);
return pxlFloattoSpherical(fx, fy, fz);
}
static inline pxlABGR8888 pxlSphericaltoABGR8888(unsigned norm) {
float azimuth = (norm & 0xff) / 256.0f * (2*M_PI);
float altitude = (((norm >> 8)& 0xff) ^ 0xff) / 255.0f * M_PI;
pxlABGR8888 pxl;
pxl.r = pxlFtoU8B(sinf(altitude) * cosf(azimuth));
pxl.g = pxlFtoU8B(sinf(altitude) * sinf(azimuth));
pxl.b = pxlFtoU8B(cosf(altitude));
pxl.a = 255;
return pxl;
}
static inline pxlARGB4444 pxlSetARGB4444(float r, float g, float b, float a) {
pxlARGB4444 ret;
ret.a = CLAMP(0,a*15, 15);
ret.r = CLAMP(0,r*15, 15);
ret.g = CLAMP(0,g*15, 15);
ret.b = CLAMP(0,b*15, 15);
//~ ret.r = pxlSatF(r)*15;
//~ ret.g = pxlSatF(g)*15;
//~ ret.b = pxlSatF(b)*15;
return ret;
}
static inline pxlARGB1555 pxlSetARGB1555(float r, float g, float b, float a) {
pxlARGB1555 ret;
ret.a = pxlSatF(a+0.5);
ret.r = pxlSatF(r)*31;
ret.g = pxlSatF(g)*31;
ret.b = pxlSatF(b)*31;
return ret;
}
static inline pxlRGB565 pxlSetRGB565(float r, float g, float b) {
pxlRGB565 ret;
ret.r = pxlSatF(r)*31;
ret.g = pxlSatF(g)*63;
ret.b = pxlSatF(b)*31;
return ret;
}
static inline pxlRGBA8888 pxlSetRGBA8888(float r, float g, float b, float a) {
pxlRGBA8888 ret;
ret.r = pxlSatF(r)*255;
ret.g = pxlSatF(g)*255;
ret.b = pxlSatF(b)*255;
ret.a = pxlSatF(a)*255;
return ret;
}
static inline pxlABGR8888 pxlSetABGR8888(float r, float g, float b, float a) {
pxlABGR8888 ret;
ret.r = pxlSatF(r)*255;
ret.g = pxlSatF(g)*255;
ret.b = pxlSatF(b)*255;
ret.a = pxlSatF(a)*255;
return ret;
}
#define PXL_ADD_SATURATE_8888(format) \
static inline format pxlAddSaturate ## format (format l, format r) { \
format ret; \
ret.r = CLAMP(0, l.r + r.r, 255); \
ret.g = CLAMP(0, l.r + r.r, 255); \
ret.b = CLAMP(0, l.r + r.r, 255); \
ret.a = CLAMP(0, l.r + r.r, 255); \
return ret; \
}
PXL_ADD_SATURATE_8888(pxlRGBA8888)
PXL_ADD_SATURATE_8888(pxlARGB8888)
PXL_ADD_SATURATE_8888(pxlABGR8888)
static inline pxlRGBA32 pxlSubRGBA32andABGR888(pxlRGBA32 l, pxlABGR8888 r) {
pxlRGBA32 ret;
ret.r = l.r - r.r;
ret.b = l.b - r.b;
ret.g = l.g - r.g;
ret.a = l.a - r.a;
return ret;
}
static inline pxlRGBA32 pxlAddRGBA32(pxlRGBA32 l, pxlRGBA32 r) {
pxlRGBA32 ret;
ret.r = l.r + r.r;
ret.b = l.b + r.b;
ret.g = l.g + r.g;
ret.a = l.a + r.a;
return ret;
}
static inline pxlRGBA32 pxlSubRGBA32(pxlRGBA32 l, pxlRGBA32 r) {
pxlRGBA32 ret;
ret.r = l.r - r.r;
ret.b = l.b - r.b;
ret.g = l.g - r.g;
ret.a = l.a - r.a;
return ret;
}
static inline pxlRGBA32 pxlMulRGBA32Float(pxlRGBA32 l, float r) {
pxlRGBA32 ret;
ret.r = l.r * r;
ret.r = l.g * r;
ret.r = l.b * r;
ret.r = l.a * r;
return ret;
}
static inline pxlRGB565 pxlConvertRGBA8888toRGB565(pxlRGBA8888 color) {
pxlRGB565 ret;
ret.r = pxlReduceRnd(color.r, 3);
ret.g = pxlReduceRnd(color.g, 2);
ret.b = pxlReduceRnd(color.b, 3);
return ret;
}
static inline pxlRGB565 pxlConvertARGB8888toRGB565(pxlARGB8888 color) {
pxlRGB565 ret;
ret.r = pxlReduceRnd(color.r, 3);
ret.g = pxlReduceRnd(color.g, 2);
ret.b = pxlReduceRnd(color.b, 3);
return ret;
}
static inline pxlRGB565 pxlConvertABGR8888toRGB565(pxlABGR8888 color) {
pxlRGB565 ret;
ret.r = pxlReduceRnd(color.r, 3);
ret.g = pxlReduceRnd(color.g, 2);
ret.b = pxlReduceRnd(color.b, 3);
return ret;
}
static inline pxlRGB565 pxlConvertRGBA32toRGB565(pxlRGBA32 color) {
pxlRGB565 ret;
ret.r = pxlReduceRnd(CLAMP(0,color.r,255), 3);
ret.g = pxlReduceRnd(CLAMP(0,color.g,255), 2);
ret.b = pxlReduceRnd(CLAMP(0,color.b,255), 3);
return ret;
}
#define pxlConvertToRGB565(X) _Generic((X), \
pxlRGBA8888: pxlConvertRGBA8888toRGB565, \
pxlARGB8888: pxlConvertARGB8888toRGB565, \
)(X)
static inline pxlARGB4444 pxlConvertRGBA8888toARGB4444(pxlRGBA8888 color) {
pxlARGB4444 ret;
ret.a = pxlReduceRnd(color.a, 4);
ret.r = pxlReduceRnd(color.r, 4);
ret.g = pxlReduceRnd(color.g, 4);
ret.b = pxlReduceRnd(color.b, 4);
return ret;
}
static inline pxlARGB4444 pxlConvertARGB8888toARGB4444(pxlARGB8888 color) {
pxlARGB4444 ret;
ret.a = pxlReduceRnd(color.a, 4);
ret.r = pxlReduceRnd(color.r, 4);
ret.g = pxlReduceRnd(color.g, 4);
ret.b = pxlReduceRnd(color.b, 4);
return ret;
}
static inline pxlARGB4444 pxlConvertABGR8888toARGB4444(pxlABGR8888 color) {
pxlARGB4444 ret;
ret.a = pxlReduceRnd(color.a, 4);
ret.r = pxlReduceRnd(color.r, 4);
ret.g = pxlReduceRnd(color.g, 4);
ret.b = pxlReduceRnd(color.b, 4);
return ret;
}
static inline pxlARGB4444 pxlConvertRGBA32toARGB4444(pxlRGBA32 color) {
pxlARGB4444 ret;
ret.r = pxlReduceRnd(CLAMP(0,color.r,255), 4);
ret.g = pxlReduceRnd(CLAMP(0,color.g,255), 4);
ret.b = pxlReduceRnd(CLAMP(0,color.b,255), 4);
ret.a = pxlReduceRnd(CLAMP(0,color.a,255), 4);
return ret;
}
#define pxlConvertToARGB4444(X) _Generic((X), \
pxlRGBA8888: pxlConvertRGBA8888toARGB4444, \
pxlARGB8888: pxlConvertARGB8888toARGB4444, \
)(X)
static inline pxlARGB1555 pxlConvertRGBA8888toARGB1555(pxlRGBA8888 color) {
pxlARGB1555 ret;
ret.a = pxlReduceRnd(color.a, 7);
ret.r = pxlReduceRnd(color.r, 3);
ret.g = pxlReduceRnd(color.g, 3);
ret.b = pxlReduceRnd(color.b, 3);
return ret;
}
static inline pxlARGB1555 pxlConvertARGB8888toARGB1555(pxlARGB8888 color) {
pxlARGB1555 ret;
ret.a = pxlReduceRnd(color.a, 7);
ret.r = pxlReduceRnd(color.r, 3);
ret.g = pxlReduceRnd(color.g, 3);
ret.b = pxlReduceRnd(color.b, 3);
return ret;
}
static inline pxlARGB1555 pxlConvertABGR8888toARGB1555(pxlABGR8888 color) {
pxlARGB1555 ret;
ret.a = pxlReduceRnd(color.a, 7);
ret.r = pxlReduceRnd(color.r, 3);
ret.g = pxlReduceRnd(color.g, 3);
ret.b = pxlReduceRnd(color.b, 3);
return ret;
}
#define pxlConvertToARGB1555(X) _Generic((X), \
pxlRGBA8888: pxlConvertRGBA8888toARGB1555, \
pxlARGB8888: pxlConvertARGB8888toARGB1555, \
)(X)
static inline pxlRGBA8888 pxlConvertRGB565toRGBA8888(pxlRGB565 color) {
pxlRGBA8888 ret;
ret.r = pxlExpand(color.r, 5);
ret.g = pxlExpand(color.g, 6);
ret.b = pxlExpand(color.b, 5);
ret.a = 0xff;
return ret;
}
static inline pxlABGR8888 pxlConvertRGB565toABGR8888(pxlRGB565 color) {
pxlABGR8888 ret;
ret.r = pxlExpand(color.r, 5);
ret.g = pxlExpand(color.g, 6);
ret.b = pxlExpand(color.b, 5);
ret.a = 0xff;
return ret;
}
static inline pxlRGBA8888 pxlConvertARGB4444toRGBA8888(pxlARGB4444 color) {
pxlRGBA8888 ret;
ret.r = pxlExpand(color.r, 4);
ret.g = pxlExpand(color.g, 4);
ret.b = pxlExpand(color.b, 4);
ret.a = pxlExpand(color.a, 4);
return ret;
}
static inline pxlABGR8888 pxlConvertARGB4444toABGR8888(pxlARGB4444 color) {
pxlABGR8888 ret;
ret.r = pxlExpand(color.r, 4);
ret.g = pxlExpand(color.g, 4);
ret.b = pxlExpand(color.b, 4);
ret.a = pxlExpand(color.a, 4);
return ret;
}
static inline pxlRGBA8888 pxlConvertARGB1555toRGBA8888(pxlARGB1555 color) {
pxlRGBA8888 ret;
ret.r = pxlExpand(color.r, 5);
ret.g = pxlExpand(color.g, 5);
ret.b = pxlExpand(color.b, 5);
ret.a = pxlExpand(color.a, 1);
return ret;
}
static inline pxlABGR8888 pxlConvertARGB1555toABGR8888(pxlARGB1555 color) {
pxlABGR8888 ret;
ret.r = pxlExpand(color.r, 5);
ret.g = pxlExpand(color.g, 5);
ret.b = pxlExpand(color.b, 5);
ret.a = pxlExpand(color.a, 1);
return ret;
}
#define pxlConvertToRGBA8888(X) _Generic((X), \
pxlRGB565: pxlConvertRGB565toRGBA8888, \
pxlARGB4444: pxlConvertARGB4444toRGBA8888, \
pxlARGB1555: pxlConvertARGB1555toRGBA8888, \
)(X)
static inline pxlARGB8888 pxlConvertRGB565toARGB8888(pxlRGB565 color) {
pxlARGB8888 ret;
ret.r = pxlExpand(color.r, 5);
ret.g = pxlExpand(color.g, 6);
ret.b = pxlExpand(color.b, 5);
ret.a = 0xff;
return ret;
}
static inline pxlARGB8888 pxlConvertARGB4444toARGB8888(pxlARGB4444 color) {
pxlARGB8888 ret;
ret.r = pxlExpand(color.r, 4);
ret.g = pxlExpand(color.g, 4);
ret.b = pxlExpand(color.b, 4);
ret.a = pxlExpand(color.a, 4);
return ret;
}
static inline pxlARGB8888 pxlConvertARGB1555toARGB8888(pxlARGB1555 color) {
pxlARGB8888 ret;
ret.r = pxlExpand(color.r, 5);
ret.g = pxlExpand(color.g, 5);
ret.b = pxlExpand(color.b, 5);
ret.a = pxlExpand(color.a, 1);
return ret;
}
#define pxlConvertToARGB8888(X) _Generic((X), \
pxlRGB565: pxlConvertRGB565toARGB8888, \
pxlARGB4444: pxlConvertARGB4444toARGB8888, \
pxlARGB1555: pxlConvertARGB1555toARGB8888, \
)(X)
static inline pxlRGBA32 pxlConvertABGR8888toRGBA32(pxlABGR8888 color) {
pxlRGBA32 ret;
ret.r = color.r;
ret.g = color.g;
ret.b = color.b;
ret.a = color.a;
return ret;
}
static inline pxlARGB8888 pxlConvertABGR8888toARGB8888(pxlABGR8888 color) {
pxlARGB8888 ret;
ret.r = color.r;
ret.g = color.g;
ret.b = color.b;
ret.a = color.a;
return ret;
}
static inline pxlRGBA32 pxlConvertRGB565toRGBA32(pxlRGB565 color) {
pxlRGBA32 ret;
ret.r = pxlExpand(color.r, 5);
ret.g = pxlExpand(color.g, 6);
ret.b = pxlExpand(color.b, 5);
ret.a = 0xff;
return ret;
}
static inline pxlRGBA32 pxlConvertARGB4444toRGBA32(pxlARGB4444 color) {
pxlRGBA32 ret;
ret.r = pxlExpand(color.r, 4);
ret.g = pxlExpand(color.g, 4);
ret.b = pxlExpand(color.b, 4);
ret.a = pxlExpand(color.a, 4);
return ret;
}
#define PXL_COLOR_WEIGHTS v4Set(.3,.59,.11, .7)
static inline unsigned pxlFindClosestColor(const pxlABGR8888 src, const pxlABGR8888 *pal, size_t palsize) {
float bestdist = 1e100;
unsigned best = 0;
v4f srcf = v4Mul(v4Set(src.r, src.g, src.b, src.a), PXL_COLOR_WEIGHTS);
for(unsigned i = 0; i < palsize; i++) {
pxlABGR8888 c = pal[i];
v4f colorf = v4Mul(v4Set(c.r, c.g, c.b, c.a), PXL_COLOR_WEIGHTS);
float dist = v4SqrDistance(colorf, srcf);
if (dist < bestdist) {
bestdist = dist;
best = i;
}
}
return best;
}

View File

@ -0,0 +1,480 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "pvr_texture.h"
#define make_mask(width) ((width != 0) ? (0xffffffffu >> (32 - (width))) : 0)
typedef struct {
unsigned int x_mask, y_mask;
unsigned int x_inc, y_inc;
} Morton2D;
#define M2DIncX(m2d, v) (((v) + (m2d).x_inc) & (m2d).x_mask)
#define M2DIncY(m2d, v) (((v) + (m2d).y_inc) & (m2d).y_mask)
#define BAD_PIXEL() assert((0 && "Bad pixel format"))
static void AssertPixelFormat(ptPixelFormat fmt) {
assert((fmt >= PT_ARGB1555 && fmt <= PT_PALETTE_8B) || fmt == PT_YUV_TWID);
}
static inline Morton2D Morton2DInit(unsigned int x_bits, unsigned int y_bits) {
Morton2D m2d;
int shared = y_bits < x_bits ? y_bits : x_bits;
int x_extra = x_bits - shared;
int y_extra = y_bits - shared;
shared = (shared-1)*2;
m2d.x_mask = 0xAAAAAAAA & make_mask(shared);
m2d.x_mask |= (make_mask(x_extra) << (shared));
m2d.x_inc = 0x2 | ~m2d.x_mask;
m2d.y_mask = 0x55555555 & make_mask(shared);
m2d.y_mask |= (make_mask(y_extra) << shared);
m2d.y_inc = 0x1 | ~m2d.y_mask;
return m2d;
}
float BytesPerPixel(ptPixelFormat format) {
switch(format) {
case PT_ARGB1555:
case PT_RGB565:
case PT_ARGB4444:
case PT_YUV:
case PT_NORMAL:
case PT_YUV_TWID:
return 2;
case PT_PALETTE_8B:
return 1;
case PT_PALETTE_4B:
return 0.5;
default:
BAD_PIXEL();
}
}
size_t UncompressedMipSize(unsigned w, unsigned h, ptPixelFormat format) {
switch(format) {
case PT_ARGB1555:
case PT_RGB565:
case PT_ARGB4444:
case PT_YUV:
case PT_YUV_TWID:
case PT_NORMAL:
return w*h*2;
case PT_PALETTE_8B:
return w*h;
case PT_PALETTE_4B:
return w*h/2;
default:
BAD_PIXEL();
}
}
unsigned VectorArea(ptPixelFormat format) {
switch(format) {
case PT_ARGB1555:
case PT_RGB565:
case PT_ARGB4444:
case PT_YUV:
case PT_YUV_TWID:
case PT_NORMAL:
return 4;
case PT_PALETTE_8B:
return 8;
case PT_PALETTE_4B:
return 16;
default:
BAD_PIXEL();
}
}
size_t TotalMipSize(ptPixelFormat format, int vq, int level) {
AssertPixelFormat(format);
return level ? CalcTextureSize(1<<(level-1), 1<<(level-1), format, 1, vq, 0) : 0;
}
size_t CalcTextureSize(int u, int v, ptPixelFormat format, int mipmap, int vq, int codebook_size_bytes) {
AssertPixelFormat(format);
if (mipmap)
v = u;
size_t texsize = u * v;
if (mipmap)
texsize = texsize *4/3 + 3;
if ((unsigned)format <= PT_NORMAL || format == PT_YUV_TWID)
texsize *= 2;
else if (format == PT_PALETTE_4B)
texsize /= 2;
else if (format == PT_PALETTE_8B)
;
else
BAD_PIXEL();
if (vq)
texsize = (texsize+7) / 8 + codebook_size_bytes;
return texsize;
}
size_t MipMapOffset(ptPixelFormat format, int vq, int level) {
AssertPixelFormat(format);
static size_t const ofs[] = {
0x00006, //1 6
0x00008, //2 8
0x00010, //4 16
0x00030, //8 48
0x000B0, //16 176
0x002B0, //32 688
0x00AB0, //64 2736
0x02AB0, //128 10928
0x0AAB0, //256 42696
0x2AAB0, //512 174768
0xAAAB0, //1024 699056
};
assert(level >= 0);
assert(level <= 10);
size_t ret = ofs[level];
if (vq)
return ret/8;
switch(format) {
case PT_ARGB1555:
case PT_RGB565:
case PT_ARGB4444:
case PT_YUV:
case PT_YUV_TWID:
case PT_NORMAL:
return ret;
case PT_PALETTE_4B:
return ret/4;
case PT_PALETTE_8B:
return ret/2;
default:
BAD_PIXEL();
}
return 0;
}
void MakeTwiddled8(void *pix, int w, int h) {
char *cpy = malloc(w*h), *dst = pix;
char *src = cpy;
memcpy(cpy, pix, w*h);
Morton2D m2d = Morton2DInit(__builtin_ffs(w), __builtin_ffs(h));
int xmorton = 0, ymorton = 0;
int i, j;
for(j = 0; j < h; j++) {
for(i = 0, xmorton = 0; i < w; i++) {
dst[xmorton | ymorton] = *src++;
xmorton = M2DIncX(m2d, xmorton);
}
ymorton = M2DIncY(m2d, ymorton);
}
free(cpy);
}
void MakeTwiddled16(void *pix, int w, int h) {
uint16_t *cpy = malloc(w*h*2), *dst = pix;
uint16_t *src = cpy;
memcpy(cpy, pix, w*h*2);
Morton2D m2d = Morton2DInit(__builtin_ffs(w), __builtin_ffs(h));
int xmorton = 0, ymorton = 0;
int i, j;
for(j = 0; j < h; j++) {
for(i = 0, xmorton = 0; i < w; i++) {
dst[xmorton | ymorton] = *src++;
xmorton = M2DIncX(m2d, xmorton);
}
ymorton = M2DIncY(m2d, ymorton);
}
free(cpy);
}
void MakeTwiddled32(void *pix, int w, int h) {
uint32_t *cpy = malloc(w*h*4), *dst = pix;
uint32_t *src = cpy;
memcpy(cpy, pix, w*h*4);
Morton2D m2d = Morton2DInit(__builtin_ffs(w), __builtin_ffs(h));
int xmorton = 0, ymorton = 0;
int i, j;
for(j = 0; j < h; j++) {
for(i = 0, xmorton = 0; i < w; i++) {
dst[xmorton | ymorton] = *src++;
xmorton = M2DIncX(m2d, xmorton);
}
ymorton = M2DIncY(m2d, ymorton);
}
free(cpy);
}
void MakeDetwiddled32(void *pix, int w, int h) {
uint32_t *cpy = malloc(w*h*4), *dst = pix;
memcpy(cpy, pix, w*h*4);
Morton2D m2d = Morton2DInit(__builtin_ffs(w), __builtin_ffs(h));
int xmorton = 0, ymorton = 0;
int i, j;
for(j = 0; j < h; j++) {
for(i = 0, xmorton = 0; i < w; i++) {
*dst++ = cpy[xmorton | ymorton];
xmorton = M2DIncX(m2d, xmorton);
}
ymorton = M2DIncY(m2d, ymorton);
}
free(cpy);
}
void MakeDetwiddled8(void *pix, int w, int h) {
char *cpy = malloc(w*h), *dst = pix;
memcpy(cpy, pix, w*h);
Morton2D m2d = Morton2DInit(__builtin_ffs(w), __builtin_ffs(h));
int xmorton = 0, ymorton = 0;
int i, j;
for(j = 0; j < h; j++) {
for(i = 0, xmorton = 0; i < w; i++) {
*dst++ = cpy[xmorton | ymorton];
xmorton = M2DIncX(m2d, xmorton);
}
ymorton = M2DIncY(m2d, ymorton);
}
free(cpy);
}
void DecompressVQ(const uint8_t *indicies, int index_cnt, const void *codebook, int cb_offset, void *dst, int auto_small_vq, int codebook_size) {
const uint64_t *cb = codebook;
uint64_t *d = dst;
int i;
for(i = 0; i < index_cnt; i++) {
short codebook_offset = 0;
if(auto_small_vq) {
codebook_offset = 256 - codebook_size;
}
d[i] = cb[(indicies[i] - codebook_offset) + cb_offset];
}
}
void ConvertFromFormatToBGRA8888(const void *src, int pixel_format, pxlABGR8888 *pal, unsigned w, unsigned h, pxlABGR8888 *dst) {
AssertPixelFormat(pixel_format);
assert(src);
assert(dst);
size_t cnt = w*h;
switch(pixel_format) {
case PT_RGB565: {
const pxlRGB565 *psrc = src;
for(size_t i = 0; i < cnt; i++)
dst[i] = pxlConvertRGB565toABGR8888(psrc[i]);
} break;
case PT_ARGB4444: {
const pxlARGB4444 *psrc = src;
for(size_t i = 0; i < cnt; i++)
dst[i] = pxlConvertARGB4444toABGR8888(psrc[i]);
} break;
case PT_ARGB1555: {
const pxlARGB1555 *psrc = src;
for(size_t i = 0; i < cnt; i++)
dst[i] = pxlConvertARGB1555toABGR8888(psrc[i]);
} break;
case PT_NORMAL: {
const uint16_t *psrc = src;
for(size_t i = 0; i < cnt; i++) {
dst[i] = pxlSphericaltoABGR8888(psrc[i]);
}
} break;
case PT_YUV_TWID: {
//YUV pixels always come in pairs
assert((cnt % 2) == 0);
const uint16_t *psrc = src;
for(size_t i = 0; i < cnt; i += 4) {
pxlABGR8888 dec[4];
ConvFromYUV(psrc[i+0], psrc[i+2], dec);
ConvFromYUV(psrc[i+1], psrc[i+3], dec+2);
dst[i+0] = dec[0];
dst[i+1] = dec[2];
dst[i+2] = dec[1];
dst[i+3] = dec[3];
}
} break;
case PT_YUV: {
//YUV pixels always come in pairs
assert((cnt % 2) == 0);
const uint16_t *psrc = src;
for(size_t i = 0; i < cnt; i += 2) {
ConvFromYUV(psrc[i+0], psrc[i+1], dst + i);
}
} break;
case PT_PALETTE_8B: {
assert(pal);
const uint8_t *psrc = src;
for(size_t i = 0; i < cnt; i++) {
dst[i] = pal[psrc[i]];
}
} break;
case PT_PALETTE_4B: {
assert(pal);
assert((cnt & 1) == 0);
assert(cnt > 1);
const uint8_t *psrc = src;
for(size_t i = 0; i < cnt/2; i++) {
dst[i*2+0] = pal[psrc[i] & 0xf];
dst[i*2+1] = pal[psrc[i] >> 4];
}
} break;
default:
BAD_PIXEL();
}
}
void ptConvertToTargetFormat(const pxlABGR8888 *src, unsigned w, unsigned h, pxlABGR8888 *pal, size_t palsize, void *dst, ptPixelFormat pixel_format) {
AssertPixelFormat(pixel_format);
assert(src);
assert(dst);
size_t cnt = w*h;
switch(pixel_format) {
case PT_RGB565: {
pxlRGB565 *pdst = dst;
for(size_t i = 0; i < cnt; i++)
pdst[i] = pxlConvertABGR8888toRGB565(src[i]);
} break;
case PT_ARGB4444: {
pxlARGB4444 *pdst = dst;
for(size_t i = 0; i < cnt; i++)
pdst[i] = pxlConvertABGR8888toARGB4444(src[i]);
} break;
case PT_ARGB1555: {
pxlARGB1555 *pdst = dst;
for(size_t i = 0; i < cnt; i++)
pdst[i] = pxlConvertABGR8888toARGB1555(src[i]);
} break;
case PT_NORMAL: {
uint16_t *pdst = dst;
for(size_t i = 0; i < cnt; i++) {
pdst[i] = pxlRGBtoSpherical(src[i].r, src[i].g, src[i].b);
}
} break;
case PT_YUV: {
//Untwiddled YUV
uint16_t *pdst = dst;
//YUV always encodes pairs
assert((cnt % 2) == 0);
for(size_t i = 0; i < cnt; i+=2) {
uint32_t yuv = ConvToYUV(src[i+0], src[i+1]);
pdst[i+0] = yuv>>16;
pdst[i+1] = yuv;
}
} break;
case PT_YUV_TWID: {
//Twiddled YUV
uint16_t *pdst = dst;
//YUV always encodes pairs
assert((cnt % 2) == 0);
for(size_t i = 0; i < cnt; i+=4) {
uint32_t yuv_top = ConvToYUV(src[i+0], src[i+2]);
uint32_t yuv_bottom = ConvToYUV(src[i+1], src[i+3]);
pdst[i+0] = yuv_top>>16;
pdst[i+1] = yuv_bottom>>16;
pdst[i+2] = yuv_top;
pdst[i+3] = yuv_bottom;
}
} break;
case PT_PALETTE_8B: {
assert(pal);
assert(palsize <= 256);
assert(palsize > 0);
uint8_t *pdst = dst;
for(size_t i = 0; i < cnt; i++) {
pdst[i] = pxlFindClosestColor(src[i], pal, palsize);
}
} break;
case PT_PALETTE_4B: {
assert(pal);
assert(palsize <= 16);
assert(palsize > 0);
uint8_t *pdst = dst;
for(size_t i = 0; i < cnt/2; i++) {
pdst[i] = (pxlFindClosestColor(src[i*2+1], pal, palsize) << 4) | pxlFindClosestColor(src[i*2], pal, palsize);
}
} break;
default:
BAD_PIXEL();
}
}
bool IsValidStrideWidth(unsigned size) {
if (size > 1024 || size == 0)
return 0;
if (size == 8 || size == 16)
return 1;
if ((size % 32) == 0)
return 1;
return 0;
}
unsigned MipLevels(int size) {
if (size == 1024)
return 11;
else if (size == 512)
return 10;
else if (size == 256)
return 9;
else if (size == 128)
return 8;
else if (size == 64)
return 7;
else if (size == 32)
return 6;
else if (size == 16)
return 5;
else if (size == 8)
return 4;
else if (size == 4)
return 3;
else if (size == 2)
return 2;
else if (size == 1)
return 1;
else
return 0;
assert(0);
}
const char * ptGetPixelFormatString(unsigned format) {
const char *name[] = {
"ARGB1555", "RGB565", "ARGB4444", "YUV422", "NORMAL", "PAL4BPP", "PAL8BPP", "INVALID"
};
if (format > PT_YUV_TWID)
format = PT_YUV;
if (format > 7)
format = 7;
return name[format];
}

View File

@ -0,0 +1,115 @@
#pragma once
#include <math.h>
#include <stdbool.h>
#include "nvmath.h"
#include "pixel.h"
#ifdef __cplusplus
extern "C" {
#endif
#define PVR_MAX_TEXTURE_WIDTH 1024
#define PVR_MAX_TEXTURE_HEIGHT 1024
#define PVR_MAX_MIPMAPS (11)
#define CHANNEL_CNT_ARGB 4
#define VECTOR_W 2
#define VECTOR_H 2
#define VECTOR_AREA (VECTOR_W * VECTOR_H)
#define VECTOR_SIZE (CHANNEL_CNT_ARGB * VECTOR_AREA)
#define PVR_CODEBOOK_ENTRY_SIZE_BYTES (8)
#define PVR_CODEBOOK_SIZE_BYTES (2048)
#define PVR_FULL_CODEBOOK (256)
typedef enum {
PT_SIZE_8,
PT_SIZE_16,
PT_SIZE_32,
PT_SIZE_64,
PT_SIZE_128,
PT_SIZE_256,
PT_SIZE_512,
PT_SIZE_1024,
} ptTextureSize;
typedef enum {
//The values for following seven formats match up with the values used by the hardware
PT_ARGB1555,
PT_RGB565,
PT_ARGB4444,
PT_YUV,
PT_NORMAL,
PT_PALETTE_4B,
PT_PALETTE_8B,
//This is not a real PVR pixel format. It's a stand in for a YUV texture that is twiddled,
//which is not encoded the same way as other twiddled formats.
//It exists so that ConvertFromFormatToBGRA8888 can know that a texture is twiddled.
PT_YUV_TWID = PT_YUV + 8,
//Don't get this confused with ptePixelFormat
} ptPixelFormat;
#define PT_PIXEL_OFFSET PT_PALETTE_8B
//Returns the number of pixels in a codebook entry for a compressed texture of a given format
unsigned VectorArea(ptPixelFormat format);
size_t TotalMipSize(ptPixelFormat format, int vq, int level);
size_t UncompressedMipSize(unsigned w, unsigned h, ptPixelFormat format);
size_t MipMapOffset(ptPixelFormat format, int vq, int level);
size_t CalcTextureSize(int u, int v, ptPixelFormat format, int mipmap, int vq, int codebook_size_bytes);
void MakeTwiddled32(void *pix, int w, int h);
void MakeTwiddled16(void *pix, int w, int h);
void MakeTwiddled8(void *pix, int w, int h);
void MakeDetwiddled8(void *pix, int w, int h);
void MakeDetwiddled32(void *pix, int w, int h);
void DecompressVQ(const uint8_t *indicies, int index_cnt, const void *codebook, int cb_offset, void *dst, int auto_small_vq, int codebook_size);
unsigned MipLevels(int size);
bool IsValidStrideWidth(unsigned size);
float BytesPerPixel(ptPixelFormat format);
void ConvertFromFormatToBGRA8888(const void *src, int pixel_format, pxlABGR8888 *pal, unsigned w, unsigned h, pxlABGR8888 *dst);
void ptConvertToTargetFormat(const pxlABGR8888 *src, unsigned w, unsigned h, pxlABGR8888 *pal, size_t palsize, void *dst, ptPixelFormat pixel_format);
const char * ptGetPixelFormatString(unsigned format);
static inline unsigned ConvToYUV(pxlABGR8888 l, pxlABGR8888 r) {
const float avgR = (l.r + r.r) / 2;
const float avgG = (l.g + r.g) / 2;
const float avgB = (l.b + r.b) / 2;
//compute each pixel's Y
int Y0 = CLAMP(0, (int)(0.299 * l.r + 0.587 * l.g + 0.114 * l.b), 255);
int Y1 = CLAMP(0, (int)(0.299 * r.r + 0.587 * r.g + 0.114 * r.b), 255);
int U = CLAMP(0, (int)(-0.169 * avgR - 0.331 * avgG + 0.4990 * avgB + 128), 255);
int V = CLAMP(0, (int)( 0.499 * avgR - 0.418 * avgG - 0.0813 * avgB + 128), 255);
unsigned yuv1 = ((uint8_t)Y0) << 8 | (uint8_t)U;
unsigned yuv2 = ((uint8_t)Y1) << 8 | (uint8_t)V;
return (yuv1 << 16) | yuv2;
}
//Writes two pixels, to dst[0] and dst[1]
static inline void ConvFromYUV(unsigned yuv1, unsigned yuv2, pxlABGR8888 *dst) {
const int Y0 = (yuv1 & 0xFF00) >> 8;
const int Y1 = (yuv2 & 0xFF00) >> 8;
const int U = (int)(yuv1 & 0xFF) - 128;
const int V = (int)(yuv2 & 0xFF) - 128;
int r, g, b;
r = CLAMP(0, (int)(Y0 + 1.375 * V), 255);
g = CLAMP(0, (int)(Y0 - 0.34375 * U - 0.6875 * V), 255);
b = CLAMP(0, (int)(Y0 + 1.71875 * U), 255);
dst[0] = pxlSetABGR8888(pxlU8toF(r), pxlU8toF(g), pxlU8toF(b), 1);
r = CLAMP(0, (int)(Y1 + 1.375 * V), 255);
g = CLAMP(0, (int)(Y1 - 0.34375 * U - 0.6875 * V), 255);
b = CLAMP(0, (int)(Y1 + 1.71875 * U), 255);
dst[1] = pxlSetABGR8888(pxlU8toF(r), pxlU8toF(g), pxlU8toF(b), 1);
}
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,932 @@
#include <stdio.h>
#include <stddef.h>
#include <assert.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#include <stdarg.h>
#include "vqcompress.h"
#include "pixel.h"
#include "stb_image.h"
#include "stb_image_resize.h"
#include "pvr_texture.h"
#include "pvr_texture_encoder.h"
#include "mycommon.h"
#include "tddither.h"
/*
Returns the pixel format for a given mipmap level
YUV can change depending on level and if the texture is twiddled, this function
returns the correct format.
Passing 1 for miplevel will can be used to get the format for any level that
isn't the 1x1 level
*/
unsigned pteGetConvertFormat(PvrTexEncoder *pte, int miplevel) {
assert(pte);
unsigned format = pte->pixel_format;
if (format == PTE_YUV) {
if (miplevel == 0 && pteHasMips(pte))
return PTE_RGB565;
if (pte->raw_is_twiddled)
return PTE_YUV_TWID;
}
return format;
}
void pteInit(PvrTexEncoder *pte) {
assert(pte);
memset(pte, 0, sizeof(*pte));
pte->mip_cnt = 0;
pte->rgb_gamma = 1.0;
pte->alpha_gamma = 1.0;
pte->codebook_size = 0;
pte->resize = PTE_FIX_NONE;
pte->mipresize = PTE_FIX_MIP_NONE;
pte->pixel_format = PTE_AUTO;
pte->auto_small_vq = false;
pte->edge_method = 0;
pte->mip_shift_correction = true;
}
void pteFree(PvrTexEncoder *pte) {
assert(pte);
SAFE_FREE(&pte->pvr_codebook);
SAFE_FREE(&pte->palette);
SAFE_FREE(&pte->final_preview);
SAFE_FREE(&pte->pvr_tex);
SAFE_FREE(&pte->pvr_tex32);
for(int i = 0; i < PVR_MAX_MIPMAPS; i++) {
SAFE_FREE(&pte->src_imgs[i].pixels);
SAFE_FREE(&pte->raw_mips[i]);
SAFE_FREE(&pte->pvr_mips[i]);
SAFE_FREE(&pte->preview_mips[i]);
}
}
void pteLoadFromFiles(PvrTexEncoder *pte, const char **fnames, unsigned filecnt) {
assert(filecnt < PVR_MAX_MIPMAPS);
unsigned maxw = 0, maxh = 0;
for(unsigned i = 0; i < filecnt ; i++) {
pteImage *img = pte->src_imgs + i;
img->w = img->h = 0;
img->channels = 4;
img->pixels = (void*)stbi_load(fnames[i], &img->w, &img->h, &img->channels, 4);
ErrorExitOn(img->pixels == NULL,
"Could not load image \"%s\", exiting\n", fnames[i]);
if (filecnt > 1) {
ErrorExitOn(!IsPow2(img->w) || !IsPow2(img->h),
"When using custom mipmaps, the size of all levels must be a power of two"
" (resize is not supported). %s has a size of %ux%u\n", fnames[i],
img->w, img->h);
ErrorExitOn(img->w != img->h,
"When using custom mipmaps, all levels must be square"
" (resize is not supported). %s has a size of %ux%u\n", fnames[i],
img->w, img->h);
}
maxw = MAX(maxw, img->w);
maxh = MAX(maxh, img->h);
}
pte->src_img_cnt = filecnt;
pte->w = maxw;
pte->h = maxh;
}
void pteMakeSquare(PvrTexEncoder *pte) {
assert(pte);
unsigned smaller = MIN(pte->h, pte->w);
unsigned larger = MAX(pte->h, pte->w);
switch(pte->mipresize) {
case PTE_FIX_MIP_NONE:
break;
case PTE_FIX_MIP_MAX:
pte->h = pte->w = MAX(pte->h, pte->w);
break;
case PTE_FIX_MIP_MIN:
pte->h = pte->w = MIN(pte->h, pte->w);
break;
case PTE_FIX_MIP_NARROW_X2:
pte->w = pte->h = MIN(smaller*2, larger);
break;
case PTE_FIX_MIP_NARROW_X4:
pte->w = pte->h = MIN(smaller*4, larger);
break;
default:
assert(0);
}
//Clamp width and height to PVR limits
pte->w = CLAMP(8, pte->w, 1024);
pte->h = CLAMP(8, pte->h, 1024);
}
int pteSetSize(PvrTexEncoder *pte) {
assert(pte);
assert(pte->w);
assert(pte->h);
switch(pte->resize) {
case PTE_FIX_NONE:
//Fail if width or height aren't valid
if (pte->w > 1024 || pte->w < 8 || pte->h > 1024 || pte->h < 8)
ErrorExit("Width and height must be between 8 and 1024, and no resize is set. Dimensions are %ix%i\n", pte->w, pte->h);
if (!pteIsStrided(pte)) {
if (!IsPow2(pte->w) || !IsPow2(pte->h))
ErrorExit("Width and height must be a power of two for non-stride textures. Dimensions are %ix%i\n", pte->w, pte->h);
} else {
if (!IsValidStrideWidth(pte->w))
ErrorExit("Width must be either 8, 16, or a multiple of 32 for stride textures. Width is %i\n", pte->w);
//~ if (!pteIsPartial(pte))
//~ assert(IsPow2(pte->h));
}
return 0;
break;
case PTE_FIX_UP:
if (!pteIsStrided(pte)) {
//Round width and height up to next higher power of two
pte->w = RoundUpPow2(pte->w);
} else {
if (pte->w > 16)
pte->w = (pte->w + 31) & ~0x1f;
else if (pte->w > 8)
pte->w = 16;
else
pte->w = 8;
}
pte->h = RoundUpPow2(pte->h);
break;
case PTE_FIX_DOWN:
if (!pteIsStrided(pte)) {
//Round width and height down to next higher power of two
pte->w = RoundDownPow2(pte->w);
} else {
if (pte->w >= 32)
pte->w = pte->w & ~0x1f;
else if (pte->w >= 16)
pte->w = 16;
else
pte->w = 8;
}
pte->h = RoundDownPow2(pte->h);
break;
case PTE_FIX_NEAREST: {
if (!pteIsStrided(pte)) {
//Round width to nearest power of two
pte->w = SelectNearest(RoundDownPow2(pte->w), pte->w, RoundUpPow2(pte->w));
} else {
if (pte->w >= 24)
pte->w = RoundNearest(pte->w, 32);
else if (pte->w >= 12)
pte->w = 16;
else
pte->w = 8;
}
//Round height to nearest power of two
pte->h = SelectNearest(RoundDownPow2(pte->h), pte->h, RoundUpPow2(pte->h));
} break;
default:
assert(0);
}
//Clamp width and height to PVR limits
pte->w = CLAMP(8, pte->w, 1024);
pte->h = CLAMP(8, pte->h, 1024);
assert((pte->w % 4) == 0);
pteLog(LOG_INFO, "Texture size: %ix%i\n", pte->w, pte->h);
return 0;
}
void pteSetCompressed(PvrTexEncoder *pte, int codebook_size) {
pte->codebook_size = codebook_size;
}
void pteConvertRawToTwiddled(PvrTexEncoder *pte) {
assert(pte);
assert(pte->stride == false);
assert(pte->mip_cnt > 0);
assert(pte->mip_cnt <= PVR_MAX_MIPMAPS);
assert(IsPow2(pte->w));
assert(IsPow2(pte->h));
FOR_EACH_MIP(pte, i) {
assert(pte->raw_mips[i]);
MakeTwiddled32(pte->raw_mips[i], mw, mh);
}
pte->raw_is_twiddled = true;
}
void pteCombineABGRData(PvrTexEncoder *pte) {
assert(pte);
assert(pte->mip_cnt > 0);
assert(pte->mip_cnt <= PVR_MAX_MIPMAPS);
SMART_ALLOC(&pte->pvr_tex32, CalcTextureSize(pte->w, pte->h, PTE_RGB565, pteHasMips(pte), 0, 0) * 2);
if (!pteIsCompressed(pte))
SMART_ALLOC(&pte->pvr_tex, CalcTextureSize(pte->w, pte->h, PTE_RGB565, pteHasMips(pte), 0, 0));
if (pteHasMips(pte)) {
assert(pte->w == pte->h);
FOR_EACH_MIP(pte, i) {
assert(pte->raw_mips[i] != NULL);
if (i == 0) {
//For 1x1 mip, fill padding with pixel
memcpy(pte->pvr_tex32 + 0, pte->raw_mips[i], mw * mh * sizeof(pxlABGR8888));
memcpy(pte->pvr_tex32 + 1, pte->raw_mips[i], mw * mh * sizeof(pxlABGR8888));
memcpy(pte->pvr_tex32 + 2, pte->raw_mips[i], mw * mh * sizeof(pxlABGR8888));
memcpy(pte->pvr_tex32 + 3, pte->raw_mips[i], mw * mh * sizeof(pxlABGR8888));
} else {
memcpy(pte->pvr_tex32 + MipMapOffset(PT_PIXEL_OFFSET, 0, i), pte->raw_mips[i], mw * mh * sizeof(pxlABGR8888));
}
}
} else {
assert(pte->raw_mips[0] != NULL);
memcpy(pte->pvr_tex32, pte->raw_mips[0], pte->w * pte->h * sizeof(pxlABGR8888));
}
}
void pteGenerateUncompressed(PvrTexEncoder *pte) {
assert(pte);
assert(pte->mip_cnt > 0);
assert(pte->mip_cnt <= PVR_MAX_MIPMAPS);
//Handle first 4 pixels seperately in case we have mipped YUV. pteGetConvertFormat will figure out the correct format from pte
ptConvertToTargetFormat(pte->pvr_tex32, 2, 2, pte->palette, pte->palette_size, pte->pvr_tex, pteGetConvertFormat(pte, 0));
//Convert the rest
ptConvertToTargetFormat(pte->pvr_tex32+4, CalcTextureSize(pte->w, pte->h, PT_PIXEL_OFFSET, pteHasMips(pte), 0, 0) - 4, 1,
pte->palette, pte->palette_size, pte->pvr_tex+(int)(4*BytesPerPixel(pte->pixel_format)), pteGetConvertFormat(pte, 1));
}
void pteDitherRaws(PvrTexEncoder *pte, float dither_amt) {
assert(pte);
assert(pte->mip_cnt > 0);
assert(pte->mip_cnt <= PVR_MAX_MIPMAPS);
assert(!pte->raw_is_twiddled);
FOR_EACH_MIP(pte, i) {
assert(pte->raw_mips[i] != NULL);
pteDither((void*)pte->raw_mips[i], mw, mh, 4, dither_amt, pteGetFindNearest(pte->pixel_format), pte->palette, pte->palette_size, pte->raw_mips[i], PTE_ABGR8888);
}
}
void pteGeneratePalette(PvrTexEncoder *pte) {
assert(pte);
assert(pte->mip_cnt > 0);
assert(pte->mip_cnt <= PVR_MAX_MIPMAPS);
assert(pte->palette == NULL);
assert(pte->palette_size > 0);
assert(pte->palette_size <= 256);
VQCompressor vqc;
vqcInit(&vqc, VQC_UINT8, 4, 1, pte->palette_size, pte->auto_small_vq);
vqcSetRGBAGamma(&vqc, pte->rgb_gamma, pte->alpha_gamma);
//Add mipmaps to compressor input
FOR_EACH_MIP(pte, i) {
uint32_t pixelcnt = mw * mh;
vqcAddPoints(&vqc, pte->raw_mips[i], pixelcnt);
}
//Do compression and save resulting palette
vqcResults result = vqcCompress(&vqc, 8);
assert(result.codebook);
pte->palette = result.codebook;
free(result.indices);
//~ for(unsigned i = 0; i < 256; i++) {
//~ printf("(%i, %i, %i) ", pte->palette[i].r, pte->palette[i].g, pte->palette[i].b, pte->palette[i].a);
//~ }
}
typedef uint64_t CBVector;
typedef pxlABGR8888 PerfCodebook[PVR_FULL_CODEBOOK][16];
//If vec is in cb, returns idx, otherwise adds to cb and returns cb_used
static unsigned AddFindVector(PvrTexEncoder *pte, CBVector *cb, pxlABGR8888 *vec, unsigned vectorarea, unsigned cb_used, unsigned offset, unsigned format) {
unsigned match = 0;
CBVector vecconv = 0;
assert(offset < vectorarea);
ptConvertToTargetFormat(vec, vectorarea , 1, pte->palette, pte->palette_size, &vecconv, format);
//~ printf("AddFindVector %016lx \n", vecconv);
CBVector compare_mask = (CBVector)-1ll;
unsigned bits_per_pixel = BytesPerPixel(format) * 8;
compare_mask <<= offset * bits_per_pixel;
CBVector compvec = vecconv;
for(match = 0; match < cb_used; match++) {
//Look for a matching entry in the perfect codebook
//~ printf("Comp %016lx to %016lx (%016lx)\n", compvec, cb[match], compare_mask);
if ((compvec & compare_mask) == (cb[match] & compare_mask))
break;
}
assert(match < PVR_FULL_CODEBOOK); //Don't overflow perfect CB
if (match == cb_used) {
//~ printf("Added %016lx at %u\n", vecconv, match);
cb[match] = vecconv;
}
return match;
}
void pteCompress(PvrTexEncoder *pte) {
assert(pte);
assert(pte->mip_cnt > 0);
assert(pte->mip_cnt <= PVR_MAX_MIPMAPS);
assert(pte->codebook_size > 0);
assert(pte->pvr_tex32 != NULL);
if (!pteIsStrided(pte)) {
assert(IsPow2(pte->w));
assert(IsPow2(pte->h));
assert(pte->raw_is_twiddled);
}
unsigned cbsize = pte->codebook_size;
const unsigned vectorarea = VectorArea(pte->pixel_format);
pteLog(LOG_DEBUG, "Codebook size is %i\n", cbsize);
//Calculate codebook size after taking out perfect mip codes
unsigned gen_perfect_mip_vectors = 0;
CBVector perfect_cb[PVR_FULL_CODEBOOK];
assert(vectorarea <= 16);
if (pteHasMips(pte)) {
if ((pte->pixel_format == PTE_PALETTE_4B || pte->pixel_format == PTE_PALETTE_8B) && pte->perfect_mips < 2) {
pteLog(LOG_DEBUG, "Need some perfect mips, so adding some\n");
pte->perfect_mips = 2;
} if (pte->pixel_format == PTE_YUV && pte->perfect_mips < 1) {
pteLog(LOG_DEBUG, "Need some perfect mips, so adding some\n");
pte->perfect_mips = 1;
}
} else {
if (pte->perfect_mips != 0) {
pteLog(LOG_WARNING, "Got --perfect-mips option, but not using any mipmaps.");
pte->perfect_mips = 0;
}
}
//Number of pixels we generate perfect mips for
const unsigned perf_mip_size_pix = TotalMipSize(PT_PIXEL_OFFSET, 0, pte->perfect_mips);
//Number of vectors we need to create. Round up for 4bpp mips
const unsigned perfect_mip_idx = (perf_mip_size_pix + vectorarea - 1) / vectorarea;
//Go through every perfect mip level and add each vector to a perfect codebook
//We do not want to add duplicate vectors
//Start at the highest level and work down, this way any incomplete levels, like the
//1x1 mip level, can potentially reuse any vectors from higher mip levels if they are the same
//If we start with the 1x1 mip and have junk in the unused part, it's unlikely we will
//find a match in the higher levels
for(int i = (perfect_mip_idx-1)*vectorarea; i >= 0; i -= vectorarea) {
pxlABGR8888 *src = pte->pvr_tex32 + i;
unsigned offset = 0;
if (pteHasMips(pte) && i == 0) {
//The start of the texture contains the smallest mip(s), which does not use the entire vector
//offset is always 3 pixels
offset = 3;
}
unsigned match = AddFindVector(pte, perfect_cb, src, vectorarea, gen_perfect_mip_vectors, offset, pteGetConvertFormat(pte, i));
if (match >= gen_perfect_mip_vectors) {
gen_perfect_mip_vectors++;
}
}
pteLog(LOG_DEBUG, "Made %u perfect vectors\n", gen_perfect_mip_vectors);
assert(gen_perfect_mip_vectors < pte->codebook_size);
cbsize -= gen_perfect_mip_vectors;
assert(cbsize <= PVR_FULL_CODEBOOK);
assert(cbsize > 0);
VQCompressor vqc;
vqcInit(&vqc, VQC_UINT8, 4, vectorarea, cbsize, pte->auto_small_vq);
vqcSetRGBAGamma(&vqc, pte->rgb_gamma, pte->alpha_gamma);
//Add uncompressed data
const unsigned perfect_mip_pixels = perfect_mip_idx * vectorarea;
const unsigned pxlcnt = CalcTextureSize(pte->w, pte->h, PT_PIXEL_OFFSET, pteHasMips(pte), 0, 0) - perfect_mip_pixels;
const unsigned inperfveccnt = (pxlcnt+vectorarea-1) / vectorarea;
if (pxlcnt % vectorarea) {
//Because of the way compressed mipmapped 4bpp works, the bottom right index
//is only partially used (2x4 is used out of 4x4), and the number of pixels
//in the texture is not a multiple of the vector size.
//~ printf("Have incomplete vector\n");
assert(pteHasMips(pte) && pte->pixel_format == PTE_PALETTE_4B);
unsigned npxlcnt = pxlcnt - pxlcnt % vectorarea; //Round down to whole vector
vqcAddPoints(&vqc, pte->pvr_tex32 + perfect_mip_pixels, npxlcnt);
//~ inperfveccnt = npxlcnt+vectorarea-1) / vectorarea;
//Expand the last 4x2 area into 4x4 by duplicating the 4x2 area
pxlABGR8888 last[4*4];
pxlABGR8888 *src = pte->pvr_tex32 + MipMapOffset(pte->pixel_format, 0, pteTopMipLvl(pte)) + pte->w*pte->h - 8;
memcpy(last, src, 8 * sizeof(pxlABGR8888));
memcpy(last+8, src, 8 * sizeof(pxlABGR8888));
vqcAddPoints(&vqc, last, 16);
} else {
//We can go ahead and add everything
//~ printf("No incomplete vectors\n");
assert(!(pteHasMips(pte) && pte->pixel_format == PTE_PALETTE_4B));
vqcAddPoints(&vqc, pte->pvr_tex32 + perfect_mip_pixels, pxlcnt);
}
//Re-add some points to increase their weight
//TODO We don't handle partial vectors which are required for 4BPP mipped, so turn off perfect mips for 4bpp mipped
if (pte->high_weight_mips && pteHasMips(pte) && pte->pixel_format == PTE_PALETTE_4B) {
pteLog(LOG_WARNING, "***Compressed mipmapped 4BPP does not currently support high weight mips***\nCreating texture without high weight mips\n");
pte->high_weight_mips = 0;
}
const unsigned highlvl = pte->mip_cnt - pte->high_weight_mips;
if (highlvl > 0 && highlvl < pte->mip_cnt) {
unsigned high_start = perfect_mip_pixels;
unsigned high_end = MipMapOffset(PT_PIXEL_OFFSET, 0, highlvl);
pteLog(LOG_DEBUG, "High weight up and including to %u\n", 1u<<(highlvl-1));
pteLog(LOG_DEBUG, "Re-adding bytes from %u to %u\n", high_start, high_end);
if (high_end > high_start && high_end < pxlcnt)
vqcAddPoints(&vqc, pte->pvr_tex32 + high_start, high_end - high_start);
else
pteLog(LOG_DEBUG, "Can't add high weight mips (start %u, end %u, pxlcnt %u)\n", high_start, high_end, pxlcnt);
}
//Do compression and save results
pteLog(LOG_DEBUG, "Doing compression %u...\n", vqc.point_cnt); fflush(stdout);
vqcResults result = vqcCompress(&vqc, 200);
pteLog(LOG_DEBUG, "Done!\n"); fflush(stdout);
assert(result.indices);
assert(result.codebook);
//Create PVR codebook
SMART_ALLOC(&pte->pvr_codebook, PVR_CODEBOOK_SIZE_BYTES);
ptConvertToTargetFormat(result.codebook, cbsize, vectorarea, pte->palette, pte->palette_size,
pte->pvr_codebook + pte->pvr_idx_offset*8, pteGetConvertFormat(pte, 1));
//Add any perfect CB vectors to end of generated CB
unsigned perfectcbofs = pte->pvr_idx_offset + pte->codebook_size - gen_perfect_mip_vectors;
void *cbend = pte->pvr_codebook + perfectcbofs * 8;
memcpy(cbend, perfect_cb, gen_perfect_mip_vectors * 8);
//Build up indices for texture
SMART_ALLOC(&pte->pvr_tex, CalcTextureSize(pte->w, pte->h, pte->pixel_format, pteHasMips(pte), 1, PVR_CODEBOOK_SIZE_BYTES));
uint8_t *texdst = pte->pvr_tex;
//Add any perfect mips
for(int i = 0; i < perfect_mip_idx; i++) {
//Go though each vector and find the matching codebook entry
pxlABGR8888 *src = pte->pvr_tex32 + i*vectorarea;
unsigned offset = 0;
if (pteHasMips(pte) && i == 0) {
//The start of the texture contains the smallest mip(s), which does not use the entire vector
//offset is always 3 pixels
offset = 3;
}
unsigned format = pteGetConvertFormat(pte, i);
unsigned match = AddFindVector(pte, perfect_cb, src, vectorarea, gen_perfect_mip_vectors, offset, format);
assert(match < gen_perfect_mip_vectors); //We should always find a match
texdst[i] = match + perfectcbofs;
}
//Convert data from VQCompressor (int32 to uint8)
for(int d = perfect_mip_idx, s = 0; s < inperfveccnt; d++, s++) {
texdst[d] = result.indices[s] + pte->pvr_idx_offset;
}
free(result.indices);
}
static pteImage * pteHighestSrcMip(PvrTexEncoder *pte) {
unsigned highest = 0;
unsigned highestw = pte->src_imgs[0].w;
for(unsigned i = 1; i < pte->src_img_cnt; i++) {
if (pte->src_imgs[i].w > highestw) {
highestw = pte->src_imgs[i].w;
highest = i;
}
}
return pte->src_imgs + highest;
}
//w is the width in pixels of the mip level to generate an image for
static pteImage pteGetShrinkLevel(PvrTexEncoder *pte, int w) {
assert(pte);
assert(pte->src_img_cnt > 0);
assert(pte->src_imgs[0].pixels);
unsigned level = MipLevels(w);
if (pte->want_mips == PTE_MIP_QUALITY)
level += 2;
int desired_width = 1 << (level);
//Default to first src_img, since it's known good
pteImage best = pte->src_imgs[0];
//If the desired width is for a level small enough that we have already created it,
//use the existing level
if (desired_width <= pte->w) {
pteLog(LOG_DEBUG, "using existing mip %i (%i)\n", desired_width, level);
best.w = best.h = desired_width;
best.pixels = pte->raw_mips[level];
}
//Check rest of src_imgs for an exact match with w, this overrides everything
for(unsigned i = 0; i < pte->src_img_cnt; i++) {
pteImage *cur = pte->src_imgs + i;
if (cur->w == w && cur->h == w) {
//Have exact match
pteLog(LOG_DEBUG, "Match\n");
best = *cur;
break;
}
}
return best;
}
void pteGenerateMips(PvrTexEncoder *pte) {
assert(pte);
assert(!pte->raw_is_twiddled); //can't generate if already twiddled, twiddle afterwards
assert(pte->mip_cnt == 0); //have no mips
//Fix size if resizing is enabled
pteMakeSquare(pte);
ErrorExitOn(pteIsStrided(pte), "Mipmapped textures must be twiddled, but have stride parameter\n");
ErrorExitOn(pte->w != pte->h, "Image must be square, but dimensions are (%ux%u)\n", pte->w, pte->h);
ErrorExitOn(!IsPow2(pte->w) || !IsPow2(pte->h), "Height and width must be a power of two, but dimensions are (%ux%u)\n", pte->w, pte->h);
assert(pte->w == pte->h); //have square
assert(IsPow2(pte->w));
assert(IsPow2(pte->h));
assert(pte->w >= 8);
pte->mip_cnt = MipLevels(pte->w);
assert(pte->mip_cnt > 0);
assert(pte->mip_cnt <= 11);
FOR_EACH_MIP_REV(pte, i) {
//Allocate space for mip level
unsigned pixelcnt = mw * mh;
assert(pte->raw_mips[i] == NULL); //Should have no raws yet
SMART_ALLOC(&pte->raw_mips[i], pixelcnt * sizeof(pxlABGR8888));
pteImage src = pteGetShrinkLevel(pte, mw);
pteLog(LOG_INFO, "Making %ux%u mip from %ux%u image\n", mw, mh, src.w, src.h);
if (src.w == mw && src.h == mh) {
memcpy(pte->raw_mips[i], src.pixels, mw * mh * sizeof(pxlABGR8888));
} else {
float shift = 0;
if (pte->mip_shift_correction)
shift = -0.5 + (float)mw / pte->w / 2;
stbir_resize_subpixel(src.pixels, src.w, src.h, 0,
pte->raw_mips[i], mw, mh, 0,
STBIR_TYPE_UINT8, //format
4, //channels
3, //alpha
0, //alpha flags (use default handling)
pte->edge_method, pte->edge_method,
STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT,
STBIR_COLORSPACE_SRGB,
//~ STBIR_COLORSPACE_LINEAR,
NULL, //alloc
(float)mw / src.w, (float)mw / src.h, //xscale, yscale,
shift, shift //xoffset, yoffset
);
}
}
}
int pteGenerateRawFromSource(PvrTexEncoder *pte) {
assert(pte);
assert(pte->src_img_cnt >= 1);
assert(pte->src_imgs[0].pixels);
assert(pte->w >= 8);
assert(pte->h >= 8);
assert(pte->w <= 1024);
assert(pte->h <= 1024);
size_t tex_raw_size = pte->w * pte->h * sizeof(pxlABGR8888);
SMART_ALLOC(&pte->raw_mips[0], tex_raw_size);
//If no input mips, set raw_mips to one level, with one image from source data
pteImage *h = pteHighestSrcMip(pte);
if (h->w == pte->w && h->h == pte->h) {
//If src size is equal to texture size, just copy the data over
pteLog(LOG_INFO, "Source size matches texture size\n");
memcpy(pte->raw_mips[0], h->pixels, tex_raw_size);
pte->mip_cnt = 1;
} else {
//If src size is different to texture size, resize source to fit texture
pteLog(LOG_INFO, "Source is getting resized from %ux%u to %ux%u\n", h->w, h->h, pte->w, pte->h);
pte->mip_cnt = 1;
stbir_resize(h->pixels, h->w, h->h, 0,
pte->raw_mips[0], pte->w, pte->h, 0,
STBIR_TYPE_UINT8, //format
4, //channels
3, //alpha
0, //alpha flags (use default handling)
pte->edge_method, pte->edge_method,
STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT,
STBIR_COLORSPACE_SRGB,
//~ STBIR_COLORSPACE_LINEAR,
NULL //alloc
);
}
return 0;
}
void pteGeneratePreviews(PvrTexEncoder *pte) {
assert(pte);
assert(pte->pvr_tex);
unsigned size_pixels = CalcTextureSize(pte->w, pte->h, PT_PIXEL_OFFSET, pteHasMips(pte), 0, 0);
void *src = pte->pvr_tex;
if (pteIsCompressed(pte)) {
//For compressed mipmapped 4bpp textures, the number of indices is not
//a multiple of the texture size. Round up the index count, and allocate
//room for an extra vector worth of pixels
src = malloc(size_pixels * 2 + 16);
unsigned vecarea = VectorArea(pte->pixel_format);
unsigned idxs = (size_pixels+vecarea-1) / vecarea;
DecompressVQ(pte->pvr_tex, idxs, pte->pvr_codebook, 0, src, pte->auto_small_vq, pte->codebook_size);
}
FOR_EACH_MIP(pte, i) {
unsigned format = pteGetConvertFormat(pte, i);
unsigned w = mw;
//For 1x1 4bpp, we need to convert two pixels in a byte, so up the size
if (format == PTE_PALETTE_4B && mw == 1)
w = 2;
//Allocate buffer for unconverted mip level
pxlABGR8888 *prev = malloc(w * mh * sizeof(pxlABGR8888));
//Get pixel data for current mip level
void *pixels = src;
if (pteHasMips(pte)) {
//We already decompressed the image, so we always pass 0 for compression here
unsigned ofs = MipMapOffset(pte->pixel_format, 0, i);
pixels += ofs;
}
//Convert image from pixels, storing in prev
ConvertFromFormatToBGRA8888(pixels, format, pte->palette, w, mh, prev);
//For 4bpp, the pixel we need is stored as the second pixel of the two from the byte we converted
if (format == PTE_PALETTE_4B && mw == 0)
prev[0] = prev[1];
//Detwiddle if using twiddled format
if (pte->raw_is_twiddled)
MakeDetwiddled32(prev, mw, mh);
//Save deconverted image to pte
SAFE_FREE(&pte->preview_mips[i]);
pte->preview_mips[i] = prev;
}
if (pteHasMips(pte)) {
//Create preview image with combined mips
assert(pte->final_preview == NULL);
unsigned mp_w = pte->w * 1.5;
pte->final_preview_w = mp_w;
SMART_ALLOC(&pte->final_preview, pte->h * mp_w * sizeof(pxlABGR8888));
pxlABGR8888 *mp = pte->final_preview;
unsigned mipy = pte->h - 1;
if (1) {
//Create preview with all mips
FOR_EACH_MIP(pte, i) {
assert(pte->preview_mips[i]);
//The highest mip level goes to the top left, the rest are on the right
//For the non-top levels, we start at the bottom then go up
unsigned mipx = pte->w;
mipy -= mh;
if (i == pteTopMipLvl(pte)) {
mipy = 0;
mipx = 0;
}
//Copy rows from preview_mips to final_preview
for(unsigned y = 0; y < mh; y++) {
memcpy(mp + (y + mipy)*mp_w + mipx, pte->preview_mips[i] + y*mw, mw * sizeof(pxlABGR8888));
}
}
} else {
//Create preview of top mip level
int i = pteTopMipLvl(pte);
for(unsigned y = 0; y < pte->h; y++) {
memcpy(mp + y*mp_w, pte->preview_mips[i] + y*pte->w, pte->w * sizeof(pxlABGR8888));
}
}
} else {
//Create preview image without mips
pte->final_preview_w = pte->w;
SMART_ALLOC(&pte->final_preview, pte->h * pte->w * sizeof(pxlABGR8888));
assert(pte->preview_mips[0]);
memcpy(pte->final_preview, pte->preview_mips[0], pte->w * pte->h * sizeof(pxlABGR8888));
}
if (pteIsCompressed(pte)) {
free(src);
}
}
void pteConvertRawHeightToNormals(PvrTexEncoder *pte) {
assert(pte);
assert(!pte->raw_is_twiddled);
FOR_EACH_MIP(pte, i) {
assert(pte->raw_mips[i]);
v3f *norms = malloc(mw * mw * sizeof(*norms));
const pxlABGR8888 *src = pte->raw_mips[i];
//Calculate normals from height map
for(int y = 0; y < mh; y++) {
for(int x = 0; x < mw; x++) {
//TODO change this so that it copies the image to a larger temp buffer with the correct
//edges, to avoid these per pixel checks and get STBIR_EDGE_ZERO working
//Wrap around offsets
unsigned l, r, u, d;
if (pte->edge_method == STBIR_EDGE_WRAP) {
l = x == 0 ? mw-1 : x-1;
r = x == (mw-1) ? 0 : x+1;
u = y == 0 ? mh-1 : y-1;
d = y == (mh-1) ? 0 : y+1;
} else if (pte->edge_method == STBIR_EDGE_CLAMP) {
l = x == 0 ? x : x-1;
r = x == (mw-1) ? x : x+1;
u = y == 0 ? y : y-1;
d = y == (mh-1) ? y : y+1;
} else if (pte->edge_method == STBIR_EDGE_REFLECT) {
l = x == 0 ? x+1 : x-1;
r = x == (mw-1) ? x-1 : x+1;
u = y == 0 ? y+1 : y-1;
d = y == (mh-1) ? y-1 : y+1;
} else {
ErrorExit("Zero edge method not supported for height maps");
}
unsigned ofs = y*mw+x;
norms[ofs].x = pxlU8toF(src[y*mw + l].r) - pxlU8toF(src[y*mw + r].r);
norms[ofs].y = pxlU8toF(src[d*mw + x].r) - pxlU8toF(src[u*mw + x].r);
norms[ofs].z = sqrtf(1 - norms[ofs].x*norms[ofs].x + norms[ofs].y*norms[ofs].y);
//Gotten some weirdness on some heightmaps if we normalize here, so it's disabled for now
//~ norms[ofs] = v3NormalizeS(norms[ofs]);
}
}
//Replace raw_mips with normals
for(int y = 0; y < mh; y++) {
for(int x = 0; x < mw; x++) {
unsigned ofs = y*mw+x;
pte->raw_mips[i][ofs].r = pxlFtoU8B(norms[ofs].x);
pte->raw_mips[i][ofs].g = pxlFtoU8B(norms[ofs].y);
pte->raw_mips[i][ofs].b = pxlFtoU8B(norms[ofs].z);
pte->raw_mips[i][ofs].a = 255;
}
}
free(norms);
}
}
void pteAutoSelectPixelFormat(PvrTexEncoder *pte) {
assert(pte);
assert(pte->src_img_cnt);
unsigned clearpix = 0, halfpix = 0, opaquepix = 0;
for(int j = 0; j < pte->src_img_cnt; j++) {
pteImage *img = pte->src_imgs + j;
for(int i = 0; i < img->w * img->h; i++) {
int a = img->pixels[i].a;
if (a == 0)
clearpix++;
else if (a == 0xff)
opaquepix++;
else
halfpix++;
}
}
if (halfpix > 0)
pte->pixel_format = PTE_ARGB4444;
else if (clearpix > 0)
pte->pixel_format = PTE_ARGB1555;
else
pte->pixel_format = pte->pixel_format == PTE_AUTO_YUV ? PTE_YUV : PTE_RGB565;
pteLog(LOG_INFO, "Selected pixel format %s\n", ptGetPixelFormatString(pte->pixel_format));
}
void pteEncodeTexture(PvrTexEncoder *pte) {
//Generate resized ABGR data from source image
if (pte->want_mips) {
pteLog(LOG_PROGRESS, "Generating mipmaps...\n");
pteGenerateMips(pte);
} else if (pte->src_img_cnt == 1) {
pteGenerateRawFromSource(pte);
} else {
ErrorExit("Multiple source images have been specified, but mipmaps have not been requested\n");
}
//If the source image is a height map, convert it to a normal map
if (pte->pixel_format == PTE_BUMP) {
pteConvertRawHeightToNormals(pte);
pte->pixel_format = PTE_NORMAL;
}
//Generate palette
if (pte->pixel_format == PTE_PALETTE_4B || pte->pixel_format == PTE_PALETTE_8B) {
if (pte->pixel_format == PTE_PALETTE_8B) {
if (pte->palette_size == 0) {
pte->palette_size = 256;
} else if (pte->palette_size > 256) {
ErrorExit("palette size must be 256 or less for 8bpp textures\n");
}
} else if (pte->pixel_format == PTE_PALETTE_4B) {
if (pte->palette_size == 0) {
pte->palette_size = 16;
} else if (pte->palette_size > 16) {
ErrorExit("palette size must be 16 or less for 4bpp textures\n");
}
}
pteLog(LOG_PROGRESS, "Generating palette...\n");
pteGeneratePalette(pte);
}
//Do dithering
if (pte->dither && pte->pixel_format != PTE_YUV) {
pteLog(LOG_PROGRESS, "Dithering...\n");
pteDitherRaws(pte, pte->dither);
}
//Twiddle if texture is not strided
if (!pte->stride) {
pteLog(LOG_PROGRESS, "Twiddling...\n");
pteConvertRawToTwiddled(pte);
} else {
//Stride textures cannot be these formats
//Normal maps kill PVR if bilinear is used, and strided palettes can't be set
if (pte->pixel_format == PTE_NORMAL || pte->pixel_format == PTE_PALETTE_4B || pte->pixel_format == PTE_PALETTE_8B)
ErrorExit("Stride textures cannot be normal maps or palettized textures\n");
}
//Convert from internal ABGR8888 to final output format
pteCombineABGRData(pte);
if (pteIsCompressed(pte)) {
pteLog(LOG_PROGRESS, "Compressing...\n");
pteCompress(pte);
pteLog(LOG_PROGRESS, "Compressed...\n");
float uncompsize = CalcTextureSize(pte->w, pte->h, PTE_RGB565, pteHasMips(pte), 0, 0);
float compsize = CalcTextureSize(pte->w, pte->h, pte->pixel_format, pteHasMips(pte), 1, pte->codebook_size*8);
pteLog(LOG_INFO, "Compression ratio: %f\n", uncompsize / compsize);
} else {
pteLog(LOG_PROGRESS, "Converting as uncompressed...\n");
pteGenerateUncompressed(pte);
}
}

View File

@ -0,0 +1,226 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include "pixel.h"
#include "pvr_texture.h"
#include "stb_image_resize.h"
typedef enum {
PTE_MIP_NONE,
PTE_MIP_QUALITY,
PTE_MIP_FAST,
} pteMipGen;
typedef enum {
PTE_FIX_NONE,
PTE_FIX_UP,
PTE_FIX_DOWN,
PTE_FIX_NEAREST,
} pteFixSizeMethod;
typedef enum {
PTE_FIX_MIP_NONE, //Must be square
PTE_FIX_MIP_NARROW_X2, //If not square, take narrower dimension and double it (512x32->64x64)
PTE_FIX_MIP_NARROW_X4, //If not square, take narrower dimension and quadruple it (512x32->128x128)
PTE_FIX_MIP_MAX, //Take largest dimension (512x32->512x512)
PTE_FIX_MIP_MIN, //Take narrowest dimension (512x32->32x32)
} pteFixMipSizeMethod;
typedef uint8_t VQIndex;
typedef enum {
//The values for following seven formats match up with the values used by the hardware
PTE_ARGB1555,
PTE_RGB565,
PTE_ARGB4444,
PTE_YUV,
PTE_NORMAL,
PTE_PALETTE_4B,
PTE_PALETTE_8B,
//The following are not real, supported PVR formats, but used internally by some functions
PTE_YUV_TWID = PT_YUV_TWID,
//The following cannot be used as a ptPixelFormat. They are used by pte* functions only
PTE_ABGR8888,
PTE_BUMP, //Signals input is height map that needs to be converted to normal map
PTE_AUTO, //Selects RGB565, ARGB1555, or ARGB4444 automatically based on alpha of input image
PTE_AUTO_YUV, //Selects YUV, ARGB1555, or ARGB4444 automatically based on alpha of input image
} ptePixelFormat;
typedef struct {
unsigned w, h;
int channels;
pxlABGR8888 *pixels;
} pteImage;
static inline size_t pteImgPixelCnt(const pteImage *img) {
return img->w * img->h;
}
static inline size_t pteImgSize(const pteImage *img) {
return img->w * img->h * sizeof(pxlABGR8888);
}
typedef struct PvrTexEncoder {
//
// The following should be set by the user before using the encoder
//
//If true, generating a nontwiddled stride texture.
//Texture width can 8, 16, or be any multiple of 32 that is less than or equal to 1024
//If false, texture will be twiddled
bool stride;
//Method of generating mipmaps, or no mipmaps generated
pteMipGen want_mips;
//Number of mipmap levels, always 1 if no mipmaps
//Not set to final mipmap count until mipmaps have been generated
unsigned mip_cnt;
//Color format of texture
ptePixelFormat pixel_format;
//Size of codebook in indicies, ranges from 0 to 256
//0 means uncompressed, >0 will result in a VQ compressed texture
unsigned codebook_size;
//Offset (in entries) into full codebook where the first element is
//If you have a 128 entry codebook, and want to use the first half, set this to zero
//If you want to the last half, set this to 128
//codebook_size + pvr_idx_offset must be <= 256
unsigned pvr_idx_offset;
unsigned perfect_mips; //number of mipmap levels to generate losslessly
bool mip_shift_correction; //preform correction for mipmap shifting
//mips between top-high_weight and perfect_mips are given extra weight (quality) when compressing,
//0 means no high weight, 1 means all mips below highest have extra priority,
//2 means all mips below second highest are given prio, etc.
unsigned high_weight_mips;
//Resize method to nearest valid size
pteFixSizeMethod resize;
//Resize method to make square
pteFixMipSizeMethod mipresize;
//Amount of dithering, 0 is none, 1 is full
float dither;
//How to downsample on the edges
stbir_edge edge_method;
//Generate small codebook size based on texture dimensions
bool auto_small_vq;
//Unprocessed source images specified by user
unsigned src_img_cnt;
pteImage src_imgs[PVR_MAX_MIPMAPS];
float rgb_gamma;
float alpha_gamma;
//
// Below here is used internally by encoder. User should avoid messing with most of these.
//
//Width and height of texture in pixels (if we have mipmaps, this is largest mip level)
unsigned w, h;
//If true, raw_mips contains twiddled data, otherwise data is normal, linear
bool raw_is_twiddled;
unsigned palette_size; //size in colors, ranges from 0 to 256
pxlABGR8888 *palette;
//Preview with all mips in one image
//height is h
unsigned final_preview_w;
pxlABGR8888 *final_preview;
//Codebook in pvr format, uses pixel_format pixel
void *pvr_codebook;
//PVR texture data, in the same format used by the pvr, including all mipmaps laid out in order, including padding
//If compressed, this is just the indices, and does NOT include the codebook
void *pvr_tex;
//Uncompressed PVR texture data, as pvr_tex, but in 32-bit color. This is generated first, then pvr_tex is generated from it
pxlABGR8888 *pvr_tex32;
//For the following three *_mips arrays...
//If mip_cnt > 1, 0 is 1x1, 1 is 2x2, 3 is 4x4...
//If mip_cnt == 1, 0 is only level, and its size is equal to this->w, this->h
//Uncompressed source, 4-bytes per pixel
pxlABGR8888 *raw_mips[PVR_MAX_MIPMAPS];
//Raw PVR data
//If texture is not compresed, this is in pixel_format
//If texture is compressed, these are indicies
uint8_t *pvr_mips[PVR_MAX_MIPMAPS];
//Output preview
//What you get after compressing and reducing color depth
pxlABGR8888 *preview_mips[PVR_MAX_MIPMAPS];
} PvrTexEncoder;
//For both FOR_EACH, width and height of current mipmap level are in variables mw and mh
//Go through each level from small to large
#define FOR_EACH_MIP(pte, mipidx) \
for(int mipidx = 0, mw = pteHasMips(pte) ? 1 : pte->w, mh = pteHasMips(pte) ? 1 : pte->h; mipidx < pte->mip_cnt; mipidx++, mw <<= 1, mh <<= 1)
//Go through each level from large to small
#define FOR_EACH_MIP_REV(pte, mipidx) \
for(int mipidx = pte->mip_cnt-1, mw = pte->w, mh = pte->h; mipidx >= 0; mipidx--, mw >>= 1, mh >>= 1)
static inline unsigned pteTopMipLvl(const PvrTexEncoder *pte) {
return pte->mip_cnt - 1;
}
static inline bool pteHasMips(const PvrTexEncoder *pte) {
return pte->want_mips != PTE_MIP_NONE;
}
static inline bool pteIsCompressed(const PvrTexEncoder *pte) {
return pte->codebook_size > 0;
}
static inline bool pteIsStrided(const PvrTexEncoder *pte) {
return pte->stride;
}
static inline bool pteIsPalettized(const PvrTexEncoder *pte) {
return pte->pixel_format == PTE_PALETTE_4B || pte->pixel_format == PTE_PALETTE_8B;
}
void pteInit(PvrTexEncoder *pte);
void pteFree(PvrTexEncoder *pte);
void pteLoadFromFiles(PvrTexEncoder *pte, const char **fnames, unsigned filecnt);
void pteEncodeTexture(PvrTexEncoder *pte);
void pteMakeSquare(PvrTexEncoder *pte);
int pteSetSize(PvrTexEncoder *pte);
void pteSetCompressed(PvrTexEncoder *pte, int codebook_size);
void pteGeneratePreviews(PvrTexEncoder *pte);
void pteAutoSelectPixelFormat(PvrTexEncoder *pte);
///////////
void ErrorExitOn(int cond, const char *fmt, ...);
void ErrorExit(const char *fmt, ...) __attribute__((noreturn));
typedef enum pteLogLevel {
//When changing these, make sure to update logtypes[] in pteLogLocV
LOG_NONE, //Disables logs, do not log to this type
LOG_WARNING, //Important warnings or errors
LOG_COMPLETION, //Encode completion
LOG_PROGRESS, //Progress of encoding
LOG_INFO, //Useful info on encoding
LOG_ALL, //Prints all normal user visible logs, do not log to this type
LOG_DEBUG, //Debug info. Must be the highest level, used as bounds check in pteLogLocV
} pteLogLevel;
#define pteLog(level, ...) //pteLogLoc(level, __FILE__, __LINE__, __VA_ARGS__)
extern void pteLogLoc(unsigned level, const char *file, unsigned line, const char *fmt, ...);

View File

@ -0,0 +1,319 @@
pvrtex converts images to Dreamcast PowerVR textures.
It is designed to work similarly to tvspelsfreak's texconv, so it can be used in place of texconv will minimal changes. It might be helpful to read the readme for texconv for additional information not covered here. In particular, there are explainations of the types of textures supported by the Dreamcast.
Compared to texconv, pvrtex has the following enhancements:
* Faster texture compression and palette generation
* Can generate small codebook VQ textures
* No Qt dependency
* Support for compressed stride textures
* Better mipmap generation
* Dithering
* Support for additional output file types (adds .PVR and .DT)
--------------------------------------------------------------------------
Usage Examples:
pvrtex -i source.png -o texture.dt
Converts a PNG file to a DT file that is twiddled, uncompressed texture, without mipmaps. The format is automatically chosen depending on alpha content of source.png (See description for AUTO texture format in command option listing).
pvrtex -i source.png -o texture.dt -f argb4444 -d -c 64 -m quality -r -R
Converts a PNG file to a DT file that is twiddled, compressed texture, with mipmaps. The texture will use the ARGB4444 color format, and dithered. If the source image is not already a square power-of-two, it will be resized to be the nearest square power-of-two. The codebook used by the compression will be limited to 64 entries out of the potential 256; this reduces quality, but reduces the size of the texture by 1.5 KB, and improves fillrate.
pvrtex -i source.png -o texture.dt -f normal -m
Converts a PNG file containing a normal map to a DT file, with mipmaps.
pvrtex -i source.png -o texture.dt -s
Converts a PNG file to a nontwiddled texture. source.png is not required to be a power-of-two width, and can also be any multiple of 32 that is <= 1024.
pvrtex -i source.png -o texture.dt -f pal8bpp -C 64 -d -p preview.png
Converts a PNG file to a DT file with 8-bit color. The resulting image will not use more than 64 colors out of the potential 256, and will be dithered. The pallete for the texture will be written to texture.dt.pal. A preview of the resulting texture will be written to preview.png.
pvrtex -i mip256.png -i mip128.png -i mip64.png -i mip32.png -i mip16.png -o texture.dt -m
Generates a mipmapped texture, using the different input images as user defined mipmap levels instead of automatically generating all of them. If a mipmap level is not defined by the user, it will be generated from a higher level. By default, the higher level will not be the level above, but three levels above; if you want to use the level above, use fast mipmaps (-m fast) instead.
--------------------------------------------------------------------------
Building:
Run "make".
:-|
To generate the proper README with linebreaks, from readme_unformatted.txt, run "make README" or "make all". Requires "fmt".
--------------------------------------------------------------------------
Command Line Options:
--help, -h
Displays help
--version, -V
Displays version
--in [filename], -i [filename]
Input image file. This option is required.
If multiple input images are specified, they are currently assumed to be different mipmap levels for a single texture. Resize options can not be used for custom mipmaps, so all images must be a square power-of-two.
Uses stb_image library for reading the image. The supported formats are:
JPEG, PNG, TGA, BMP, PSD, GIF, HDR, PIC, PMN
--out [filename], -o [filename]
Sets the file name of the converted texture. The extension of this filename controls the file format.
The supported formats are:
.PVR
Official PowerVR texture. This is incomplete as was created to help test the output of the converter, by using PC PVR viewing programs to check the resulting texture. There are likely incompatiblities with .PVR handling of official games (for example, pvrtex does not add a GBIX chunk).
.TEX
Format used by tvspelsfreak's texconv. pvrtex will generate certain formats not supported by texconv but representable in the file format (like compressed stride textures).
.DT
New file format used by this program. Supports small codebook VQ, and texture data is aligned to a 32-byte boundry to make DMA easier.
It's possible to specify no output file if only a preview image is desired.
--format [type], -f [type]
Sets the pixel format of the resulting texture.
[type] can be one of the following:
RGB565
Best color out of standard formats without sacrificing speed, but can have rainbowing on grayscale images.
ARGB1555
Allows for fully transparent texels. Better choice for grayscale than RGB565, which can have rainbowing.
ARGB4444
Allows for alpha gradients, but poorest color depth with noticable banding.
YUV422 / YUV
Better than RGB565 or ARGB1555 for gradients, but bi/trilinear filtering has worse performance.
PAL8BPP
Maximum of 256 colors. Palette is generated as a seperate file. Must be twiddled. If compression, mipmapping, and bi/trilinear are used, a hardware bug causes some texels to be filtered incorrectly on the top left/bottom right corners of a 4x2 block.
PAL4BPP
Maximum of 16 colors. Palette is generated as a seperate file. Must be twiddled. If compression, mipmapping, and bi/trilinear are used, a hardware bug causes some texels to be filtered incorrectly on the top left/bottom right corners of a 4x4 block
BUMPMAP
Generates PVR normal map. Source image is treated as a height map.
NORMAL
Generates PVR normal map. Source image is treated as a DOT3 normal map, with RGB channels corresponding to the normal's XYZ.
AUTO
Selects RGB565, ARGB1555, or ARGB4444 depending on alpha content of input image. If fully opaque, RGB565 is used, all pixels have either fully opaque or fully transparent alpha, ARGB1555 is used, and if some pixels have non-fully opaque or transparent pixels, ARGB4444 is used.
AUTOYUV
Same as AUTO, but usage of RGB565 is replaced with YUV422.
--preview [filename], -p [filename]
Generates a preview of the resulting texture file. You can see the results of bit depth reduction, dithering, mipmaps, and compression.
The preview is can be a PNG, JPG, BMP, or TGA file. Preview JPGs are generally not a good choice do to the lack of alpha, and possiblility of compression artifacts.
--compress [codebook_size / "small"], -c [codebook_size / "small"]
Generates a VQ compressed texture.
codebook_size is an optional parameter adjusts the size of the codebook generated for the texture. Reducing the codebook_size can improve fillrate, and, with .PVR and .DT files, improve the compression ratio of small textures. By default, a full codebook is used to generate the best quality texture. codebook_size can be a number from 1 to 256, or the string "small".
For .PVR files, using a number will never reduce the size of the texture, but can improve fillrate. Specifying "small" as the codebook size will reduce the texture size for certain textures smaller than 64x64 without mipmaps, or 32x32 with mipmaps.
For .TEX files, codebook_size will never reduce the size of the texture, but can still improve performance.
For .DT files, reducing the codebook_size will reduce the size of the texture. Specifying "small" as the codebook size will select a smaller codebook automatically for textures that are 128x128 or smaller, with a size of 192 for a 128x128 mipmapped texture, down to 10 for an 8x8 non-mipped texture.
--max-color [colors], -C [colors]
This limits the number of colors used for a PAL8BPP or PAL4BPP format texture. This option could be used to generate an 8BPP texture that only uses 64 colors, and the unused colors could be used for other textures.
--mipmap ["fast"], -m ["fast"]
With this option, the resulting file will have mipmaps.
By default, a Mitchell-Netravalli filter will be used on a level 3 steps above. (e.g. 64x64 will be generated from 512x512)
Adding the parameter "fast" to this option, each mipmap level is generated by downsampling the level above. (e.g. 64x64 level will be generated from 128x128) This speeds up mipmap generation for large textures.
When generating high quality mipmaps for a resized image, the largest mips will be generated directly from the source image. (i.e. with a 1600x500 source image, converted with "-m -r near -R x2", pvrtex will create a 1024x1024 texture, with mipmap levels 512x512 and 256x256 generated directly from the 1600x500 source, and not the 1024x1024 top most level).
--no-mip-shift, -S
When generating mipmaps, by default, pvrtex will preform a subpixel adjustment during downsampling to ensure the mipmaps line up correctly. This option will disable this.
--perfect-mip [levels], -M [levels]
When using mipmaps and compression, small mipmap levels will be loselessly compressed.
The levels parameter controls how many levels are lossless. 1 means only the 1x1 level will be losslessly compressed, 2 means 1x1 and 2x2 will be loselessly compressed, and so on.
Generating lossless mipmaps use up VQ codebook slots. These are the total number of codebook entries used for a given number of lossless mipmaps:
16-bit 8-bit 4-bit
1 level (1x1) 1 1 1
2 levels (2x2) 2 1 1
3 levels (4x4) 6 3 2
4 levels (8x8) 22 11 6
5 levels (16x16) 86 43 22
6 levels (32x32) -- 171 86
--high-weight [levels], -H [levels]
When using mipmaps and compression, this increases the weight the compressor gives smaller mipmap levels, to encourage the compressor to generate them at higher quality, at the cost of lower quality higher levels. Not currently supported for 4BPP textures.
The levels parameter controls how many levels below the largest have extra weight. A value of 1 means every level besides the top has boosted weight, a value of 2 means the two largest levels have normal weight, while every smaller level is boosted.
--dither [amount], -d [amount]
Enables dithering. Currently, Floyd-Steinberg is used.
Amount is an optional parameter that adjusts the amount of dithering, and is a decimal value from 0 to 1. 0 will result in no dithering, while 1 results in full dithering. If dithering is enabled but an amount is not specified, full dithering is used.
This option has no effect on YUV textures, but is valid on all others.
--stride, -s
Output a non-twiddled texture. This also allows for non-power-of-two sized textures. Width must still 8, 16, or a multiple of 32 less than or equal to 1024. Any height can be used, from 1 to 1024.
If a texture has a power-of-two dimensions and --stride is used, the resulting texture will be a nontwiddled texture that can be rendered without stride, for formats that support such textures. (.DT and .TEX support this, .PVR does not.)
Stride textures do not wrap as normal if the width is not a power-of-two, and have worse rendering performance than twiddled textures (especially when filtered or rotated). It is not possible to generate palettized or normal textures with stride. .PVR files cannnot use stride.
Valid widths for stride texture:
8, 16, 32, 64, 96, 128, 160, 192, 224, 256, 320, 352, 384, 416, 480, 512, 544, 576, 608, 640, 672, 704, 736, 768, 800, 832, 864, 896, 928, 960, 992, 1024
--resize [method], -r [method]
Resize a input image that is not a supported PVR texture size to a valid size.
If the texture is not strided, the texture will be resized to a power-of-two on both dimensions. For stride textures, width will be adjusted to an appropriate stride size, and the height will always be resized to a power-of-two.
Method controls how the image will be resized.
NONE
Generates an error if input image is not a valid size. This is the default.
NEAR
Round size up or down to nearest valid size. If resize is enabled, but no method is specified, this is the default.
UP
Round size up to next valid size
DOWN
Round size down to next valid size
Examples for non-stride textures:
Source size NONE NEAR UP DOWN
256x256 256x256 256x256 256x256 256x256
260x260 Error 256x256 512x512 256x256
200x200 Error 256x256 256x256 128x128
200x260 Error 256x256 256x512 128x256
2000x2000 Error 1024x1024 1024x1024 1024x1024
1x1 Error 8x8 8x8 8x8
--mip-resize [method], -R [method]
When using mipmaps, resizes nonsquare images to be square. This option does nothing if not using mipmaps or the image is already square (after --resize). This new size calculation occurs after the standard --resize. Source images are only resized once. This option will not resize the image to a power-of-two size if it's not already (use --resize for that).
Method controls how the image will be resized.
NONE
Generates an error if input image is not a valid mipmap size. This is the default.
X2
Doubles the narrower dimension. A 256x32 image will be resized to 64x64. If mip-resize is enabled, but no method is specified, this is the default.
X4
Quaduples or doubles the narrower dimension. A 256x32 image will be resized to 128x128.
UP
Resizes the narrower dimension to be the same size as the wider. A 256x32 image will be resized to 256x256.
DOWN
Resizes the wider dimension to be the same size as the narrower. A 256x32 image will be resized to 32x32.
Examples:
Source size X2 X4 UP DOWN
256x256 256x256 256x256 256x256 256x256
256x128 256x256 256x256 256x256 128x128
256x64 128x128 256x256 256x256 64x64
256x32 64x64 128x128 256x256 32x32
1024x8 16x16 32x32 1024x1024 8x8
--edge [type], -e [type]
Controls how the edges of the image are handled when resizing. This also affects height map to normal map conversion.
Valid options:
CLAMP
Samples are clamped to edge of image, default if not mipmapped.
WRAP
Samples wrap around to other side of image, default if mipmaps are used. Is works well when the texture is used to that it repeats, but might cause noticble bleeding around the edge of the texture in certain situations. For example, a poster or sign that doesn't repeat across the polygon. In that cause, CLAMP should be used.
REFLECT
Samples reflect off edge of image back into valid area. If you use are planning on using UV mirroring instead of wrapping, use this instead of wrap.
ZERO
Outside of image is treated as transparent blackness. This is not currently supported for images with a --type of BUMPMAP.
The default is CLAMP if not using mipmaps, or WRAP if mipmaps are used.
--bilinear, -b
In texconv, this was used to generate mipmaps with a box filter. This option is ignored in pvrtex, which currently always uses a Mitchell-Netravalli filter.
--nearest, -n
In texconv, this was used to generate mipmaps by point sampling. This option is not supported in pvrtex, and will cause pvrtex to abort.
--vqcodeusage (Not supported)
This option from texconv is not recognized at all by pvrtex.
--------------------------------------------------------------------------
.DT File Format
See file_dctex.h for documentation. file_dctex.h can also be used as a library to help access information from the file's header.
--------------------------------------------------------------------------
Known bugs
High weight compressed mips (--high-weight) does not currently work with 4BPP textures; the parameter will be ignored if specified.
There is a weird pathological performance issue with compression on certain textures. With a 1024x1024 texture with mips that is ff000000 on the left side, and ffffffff on the right side, performance drops by around 15x. It is apparently cache missing constantly, according to cachegrind, in a function that reads memory linearly (distance_limited in elbg.c). Doesn't make sense.
--------------------------------------------------------------------------
Future Ideas
* Code clean up
* Add Yliluoma dithering
* Improve error checking
* It might be possible to improve VQ quality by compressing 4/5/6 bit color instead of 8 bit (so the compressor won't waste codebook space on colors that are too similar to distinguish at given bit depth)
* Speed up compression by not processing alpha for opaque textures
* Add ability to generate a single palette to be shared for multiple textures
* Allow specifying palette format
* Allow specifiying custom external palette for texture
* Add ability to generate animated VQ textures with shared codebook for all frames
* Auto generate output name (e.g. -i image.png -o $.dt will output image.dt)
* Add wildcard input files (e.g. -i *.png)
* Add VQ index dithering
* Add KIMG output support
* Allow texture formats (PVR/TEX/DT/KMG) to be used as input formats, to transcode or recompress textures
* Add per-axis edge sampling control
* Rework code so that this can be used as a library
* Allow sizing to arbitary size (i.e. --resize 256x256)
* Add a filter to try to hide the palettized compressed mipmap bug
--------------------------------------------------------------------------
History:
Version 1.01
Program now displays error message when an error occurs loading a source image. Previously, the program would hit an assertion.
Fixed "--resize down" option. Previously, the program would round down sizes that were already a power-of-two, now sizes that are already POT are left unchanged.
Included a missing FFmpeg header file.
Version 1.0
Initial release
--------------------------------------------------------------------------
License:
This uses code from FFmpeg, which is LGPL, so this project is also LGPL. Files not originating from FFmpeg can also be used as public domain code/BSD/MIT/whatever.

7897
dreamcast/pvrtex/stb_image.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,3 @@
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,3 @@
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#include "stb_image_resize.h"

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,3 @@
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"

231
dreamcast/pvrtex/tddither.c Normal file
View File

@ -0,0 +1,231 @@
#include <stdio.h>
#include <stddef.h>
#include <assert.h>
#include <string.h>
#include <math.h>
#include "pixel.h"
#include "pvr_texture_encoder.h"
#include "pvr_texture.h"
#include "tddither.h"
void pteDNearestARGB4444(const float *sample, int sample_size, const pxlABGR8888 *palette, size_t palette_size, float *nearest_dst) {
(void)palette;
(void)sample_size;
v4f t = v4DivS(v4Float(v4Int(v4AddS(v4MulS(v4Get(sample), 16), 0.5))), 16);
nearest_dst[0] = t.x;
nearest_dst[1] = t.y;
nearest_dst[2] = t.z;
nearest_dst[3] = t.w;
}
void pteDNearestARGB1555(const float *sample, int sample_size, const pxlABGR8888 *palette, size_t palette_size, float *nearest_dst) {
(void)palette; (void)sample_size;
v4f t = v4Mul(v4Get(sample), v4Set(32,32,32,1)); //Scale by bit depth
t = v4Float(v4Int(v4AddS(t, 0.5))); //Round to nearest
t = v4Div(t, v4Set(32,32,32,1)); //Unscale by bit depth
nearest_dst[0] = t.x;
nearest_dst[1] = t.y;
nearest_dst[2] = t.z;
nearest_dst[3] = t.w;
}
void pteDNearestRGB565(const float *sample, int sample_size, const pxlABGR8888 *palette, size_t palette_size, float *nearest_dst) {
(void)palette; (void)sample_size;
v4f t = v4Mul(v4Get(sample), v4Set(32,64,32,0)); //Scale by bit depth
t = v4Float(v4IntRnd(t)); //Round to nearest
t = v4Div(t, v4Set(32,64,32,0.5)); //Unscale by bit depth
nearest_dst[0] = t.x;
nearest_dst[1] = t.y;
nearest_dst[2] = t.z;
nearest_dst[3] = t.w;
}
void pteDNearestNorm(const float *sample, int sample_size, const pxlABGR8888 *palette, size_t palette_size, float *nearest_dst) {
int norm = pxlRGBtoSpherical(sample[0] * 255.0f, sample[1] * 255.0f, sample[2] * 255.0f);
pxlABGR8888 n = pxlSphericaltoABGR8888(norm);
nearest_dst[0] = pxlU8toF(n.r);
nearest_dst[1] = pxlU8toF(n.g);
nearest_dst[2] = pxlU8toF(n.b);
nearest_dst[3] = 1;
}
void pteDNearest8BPP(const float *sample, int sample_size, const pxlABGR8888 *palette, size_t palette_size, float *nearest_dst) {
v4f cf = v4MulS(v4Get(sample), 1);
pxlABGR8888 c = pxlSetABGR8888(v4Pass(cf));
unsigned idx = pxlFindClosestColor(c, palette, palette_size);
pxlABGR8888 *nc = ((pxlABGR8888*)palette) + idx;
nearest_dst[0] = nc->r / 255.;
nearest_dst[1] = nc->g / 255.;
nearest_dst[2] = nc->b / 255.;
nearest_dst[3] = nc->a / 255.;
}
void pteConvertFPtoARGB4444(const float *img, unsigned w, unsigned h, unsigned channels, const pxlABGR8888 *palette, size_t palette_size, void * restrict dst) {
assert(channels == 4);
assert(dst);
assert(img);
pxlARGB4444 *cdst = dst;
for(unsigned y = 0; y < h; y++) {
for(unsigned x = 0; x < w; x++) {
int ofs = (y*w+x)*channels;
cdst[y*w+x] = pxlSetARGB4444(img[ofs + 0], img[ofs + 1], img[ofs + 2], img[ofs + 3]);
}
}
}
void pteConvertFPtoARGB1555(const float *img, unsigned w, unsigned h, unsigned channels, const pxlABGR8888 *palette, size_t palette_size, void * restrict dst) {
assert(channels == 4);
assert(dst);
assert(img);
pxlARGB1555 *cdst = dst;
for(unsigned y = 0; y < h; y++) {
for(unsigned x = 0; x < w; x++) {
int ofs = (y*w+x)*channels;
cdst[y*w+x] = pxlSetARGB1555(img[ofs + 0], img[ofs + 1], img[ofs + 2], img[ofs + 3]);
}
}
}
void pteConvertFPtoRGB565(const float *img, unsigned w, unsigned h, unsigned channels, const pxlABGR8888 *palette, size_t palette_size, void * restrict dst) {
assert(channels == 4);
assert(dst);
assert(img);
pxlRGB565 *cdst = dst;
for(unsigned y = 0; y < h; y++) {
for(unsigned x = 0; x < w; x++) {
int ofs = (y*w+x)*channels;
cdst[y*w+x] = pxlSetRGB565(img[ofs + 0], img[ofs + 1], img[ofs + 2]);
}
}
}
void pteConvertFPtoABGR8888(const float *img, unsigned w, unsigned h, unsigned channels, const pxlABGR8888 *palette, size_t palette_size, void * restrict dst) {
assert(channels == 4);
assert(dst);
assert(img);
pxlABGR8888 *cdst = dst;
for(unsigned y = 0; y < h; y++) {
for(unsigned x = 0; x < w; x++) {
int ofs = (y*w+x)*channels;
cdst[y*w+x] = pxlSetABGR8888(img[ofs + 0], img[ofs + 1], img[ofs + 2], img[ofs + 3]);
}
}
}
#define MAX_CHANNELS (4*4*4)
#define VGAMMA ((float)(1))
#define RVGAMMA (1.0f/VGAMMA)
void pteDither(const unsigned char *src, unsigned w, unsigned h, unsigned channels, float dither_amt, dithFindNearest nearest, const pxlABGR8888 *palette, size_t palette_size, void *dst, int dst_pixel_format) {
assert(src);
assert(dst);
assert(channels < MAX_CHANNELS);
//Convert image to floats
float *imgf = malloc(sizeof(float) * w * h * channels);
//printf("Dither %ux%ux%u\n",w,h,channels);
//printf("%zu -> %p\n",sizeof(float) * w * h * channels, imgf);
for(unsigned y = 0; y < h; y++) {
for(unsigned x = 0; x < w; x++) {
int ofs = (y*w+x)*channels;
for(unsigned c = 0; c < channels; c++) {
//~ imgf[ofs+c] = src[ofs+c] / 255.0f;
imgf[ofs+c] = pow(src[ofs+c] / 255.0f, VGAMMA);
}
}
}
if (dither_amt != 0) {
//Dither floating point image
float near[4*4*4];
float err[4*4*4];
for(unsigned y = 0; y < h; y++) {
for(unsigned x = 0; x < w; x++) {
float *cur = imgf + (y*w+x)*channels;
//~ nearest(cur, channels, palette, cur);
//~ continue;
nearest(cur, channels, palette, palette_size, &near[0]);
//~ continue;
for(int i = 0; i < channels; i++) {
err[i] = CLAMP(0, cur[i] - near[i], 1);
cur[i] = near[i];
}
/*
.0
123
*/
#define diffuse(xo,yo, weight) do {\
if ((x+(xo)) < w && (int)(x+(xo)) >= 0 && (y+(yo)) < h) \
for(int i = 0; i < channels; i++) \
cur[(w*(yo)+(xo)) * channels + i] += err[i] * (weight); \
} while(0)
if (1) {
diffuse(1, 0, 7/16. * dither_amt);
diffuse(-1, 1, 3/16. * dither_amt);
diffuse(0, 1, 5/16. * dither_amt);
diffuse(1, 1, 1/16. * dither_amt);
} else {
diffuse( 1, 0, 8/42. * dither_amt);
diffuse( 2, 0, 4/42. * dither_amt);
diffuse(-2, 1, 2/42. * dither_amt);
diffuse(-1, 1, 4/42. * dither_amt);
diffuse( 0, 1, 8/42. * dither_amt);
diffuse( 1, 1, 4/42. * dither_amt);
diffuse( 2, 1, 2/42. * dither_amt);
diffuse(-2, 2, 1/42. * dither_amt);
diffuse(-1, 2, 2/42. * dither_amt);
diffuse( 0, 2, 4/42. * dither_amt);
diffuse( 1, 2, 2/42. * dither_amt);
diffuse( 2, 2, 1/42. * dither_amt);
}
}
}
}
//Undo gamma correction
for(unsigned y = 0; y < h; y++) {
for(unsigned x = 0; x < w; x++) {
unsigned ofs = (y*w+x)*channels;
for(unsigned c = 0; c < channels; c++) {
//~ imgf[ofs+c] = pow(CLAMP(0, imgf[ofs+c], 1), RVGAMMA);
imgf[ofs+c] = pow(imgf[ofs+c], RVGAMMA);
}
}
}
switch(dst_pixel_format) {
case PTE_ARGB4444: pteConvertFPtoARGB4444(imgf, w, h, channels, palette, palette_size, dst); break;
case PTE_ARGB1555: pteConvertFPtoARGB1555(imgf, w, h, channels, palette, palette_size, dst); break;
case PTE_RGB565: pteConvertFPtoRGB565(imgf, w, h, channels, palette, palette_size, dst); break;
case PTE_ABGR8888: pteConvertFPtoABGR8888(imgf, w, h, channels, palette, palette_size, dst); break;
}
free(imgf);
}
dithFindNearest pteGetFindNearest(ptePixelFormat format) {
static const dithFindNearest tbl[] = {
&pteDNearestARGB1555,
&pteDNearestRGB565,
&pteDNearestARGB4444,
NULL,
&pteDNearestNorm,
&pteDNearest8BPP,
&pteDNearest8BPP,
};
assert(format >= PTE_ARGB1555 && format <= PTE_PALETTE_8B && format != PTE_YUV);
return tbl[format];
}

View File

@ -0,0 +1,13 @@
#pragma once
#include "pvr_texture.h"
//Dithering nearest converters
void tdDNearestARGB4444(const float *sample, int sample_size, const pxlABGR8888 *palette, size_t palette_size, float *nearest_dst);
void tdDNearestARGB1555(const float *sample, int sample_size, const pxlABGR8888 *palette, size_t palette_size, float *nearest_dst);
void tdDNearestRGB565(const float *sample, int sample_size, const pxlABGR8888 *palette, size_t palette_size, float *nearest_dst);
typedef void (*dithFindNearest)(const float *sample, int sample_size, const pxlABGR8888 *palette, size_t palette_size, float *nearest_dst);
dithFindNearest pteGetFindNearest(ptePixelFormat format);
void pteDither(const unsigned char *src, unsigned w, unsigned h, unsigned channels, float dither_amt, dithFindNearest nearest, const pxlABGR8888 *palette, size_t palette_size, void *dst, int dst_pixel_format);

View File

@ -0,0 +1,164 @@
#include <assert.h>
#include <stddef.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <math.h>
#include "vqcompress.h"
#include "elbg.h"
#include "mycommon.h"
void vqcInit(VQCompressor *c, vqcFormat input_format, unsigned channels, unsigned pix_per_cb, unsigned cb_size, unsigned auto_small_vq) {
assert(c);
memset(c, 0, sizeof(*c));
c->format = input_format;
c->channels = channels;
c->pix_per_cb = pix_per_cb;
c->cb_size = cb_size;
c->auto_small_vq = auto_small_vq;
c->dimensions = pix_per_cb * channels;
for(int i = 0; i < VQC_MAX_CHANNELS; i++)
c->gamma[i] = 1.0f;
}
/*
The idea was to cale the pixel values so that we don't loose detail when doing integer gamma correction.
Setting this too high can cause ELBG to select incorrect codebook entries.
It seems not scaling at all results in the best quality.
*/
#define INT_SCALE (255.0f)
void vqcAddPoints(VQCompressor *c, const void *src, size_t pixel_cnt) {
assert(c);
assert(src);
assert(c->format == VQC_UINT8);
assert((pixel_cnt % c->pix_per_cb) == 0); //Make sure pixel_cnt is multiple of codebook entry size
size_t point_cnt = pixel_cnt / c->pix_per_cb;
//Ensure room for new pixels
size_t space_needed = (c->point_cnt + point_cnt) * sizeof(int) * c->dimensions;
if (c->data_space <= space_needed) {
//At least 64KB, or the exact amount needed if we need more than that.
size_t more = space_needed < 64*1024 ? 64*1024 : space_needed;
void *newdata = realloc(c->data, c->data_space + more);
assert(newdata);
c->data = newdata;
}
//Convert source data to ints for ELBG
const unsigned char *srcc = src;
int *dst = c->data + c->point_cnt * c->dimensions;
size_t elem_cnt = pixel_cnt * c->channels;
unsigned curchannel = 0;
for(size_t i = 0; i < elem_cnt; i++) {
//Get source value and convert to floating point
float v = srcc[i] / 255.0f;
//Gamma correction
v = pow(v, c->gamma[curchannel++]);
if (curchannel >= c->channels)
curchannel = 0;
//Scale to fixed point
dst[i] = v * INT_SCALE;
}
c->point_cnt += point_cnt;
}
void vqcSetChannelGamma(VQCompressor *c, unsigned channel, float val) {
assert(c);
assert(channel < c->channels);
assert(val > 0);
c->gamma[channel] = val;
}
void vqcSetRGBAGamma(VQCompressor *c, float rgb, float alpha) {
assert(c);
assert(c->channels == 3 || c->channels == 4);
c->gamma[0] = c->gamma[1] = c->gamma[2] = rgb;
if (c->channels == 4)
c->gamma[3] = alpha;
}
void vqcSetARGBGamma(VQCompressor *c, float rgb, float alpha) {
assert(c);
assert(c->channels == 3 || c->channels == 4);
if (c->channels == 4) {
c->gamma[0] = alpha;
c->gamma[1] = c->gamma[2] = c->gamma[3] = rgb;
} else {
c->gamma[0] = c->gamma[1] = c->gamma[2] = rgb;
}
}
vqcResults vqcCompress(VQCompressor *c, int quality) {
assert(c);
assert(c->cb_size);
assert(c->dimensions);
assert(c->point_cnt > 0);
assert(c->data);
//Allocate indicies and int codebook result buffer
vqcResults result;
const size_t cb_elem_cnt = c->cb_size * c->dimensions;
int *int_codebook = malloc(cb_elem_cnt * sizeof(int));
result.indices = malloc(c->point_cnt * sizeof(int));
assert(int_codebook);
assert(result.indices);
//Run ELBG to generate codebook
struct ELBGContext *elbgcxt = 0;
struct AVLFG randcxt;
av_lfg_init(&randcxt, 1);
int errval = avpriv_elbg_do(&elbgcxt, c->data, c->dimensions, c->point_cnt, int_codebook, c->cb_size, quality, result.indices, &randcxt, 0);
if(c->auto_small_vq) {
// Adjust the codebook indices for small VQ
short codebook_offset = 256 - c->cb_size;
for(int i = 0; i < c->point_cnt; i++) {
result.indices[i] += codebook_offset;
}
}
assert(errval == 0);
avpriv_elbg_free(&elbgcxt);
//Convert int_codebook to input_format
assert(c->format == VQC_UINT8);
char *dst = result.codebook = malloc(c->cb_size * c->dimensions);
assert(dst);
unsigned curchannel = 0;
//Invert gamma values
float invgamma[VQC_MAX_CHANNELS];
for(int i = 0; i < c->channels; i++) {
invgamma[i] = 1.0f / c->gamma[i];
}
for(size_t i = 0; i < cb_elem_cnt; i++) {
//Get source value and convert to floating point
float v = int_codebook[i] / INT_SCALE;
//Undo gamma correction
v = pow(v, invgamma[curchannel++]);
if (curchannel >= c->channels)
curchannel = 0;
//Scale to fixed point
dst[i] = v * 255.0f;
}
//Clean up
free(int_codebook);
SAFE_FREE(&c->data);
c->data_space = 0;
return result;
}

View File

@ -0,0 +1,38 @@
#pragma once
#define VQC_MAX_CHANNELS 4
typedef enum {
VQC_UINT8,
} vqcFormat;
typedef struct {
vqcFormat format;
unsigned channels; //number of channels per pixel
unsigned pix_per_cb; //number of pixels per cb entry
unsigned point_cnt; //number of pixels / pix_per_cb
unsigned cb_size; //number of entries in cb
unsigned auto_small_vq; // auto small codebook
unsigned dimensions; //pix_per_cb * channels
//channels can have different gammas (alpha could be 1.0, while RGB could be 2.2)
float gamma[VQC_MAX_CHANNELS];
size_t data_space;
int *data; //data to compress
} VQCompressor;
typedef struct {
//Both codebook and indices may be NULL on error
void *codebook;
int *indices;
} vqcResults;
void vqcInit(VQCompressor *c, vqcFormat input_format, unsigned channels, unsigned pix_per_cb, unsigned cb_size, unsigned auto_small_vq);
void vqcAddPoints(VQCompressor *c, const void *src, size_t pixel_cnt);
void vqcSetChannelGamma(VQCompressor *c, unsigned channel, float val);
void vqcSetRGBAGamma(VQCompressor *c, float rgb, float alpha);
void vqcSetARGBGamma(VQCompressor *c, float rgb, float alpha);
vqcResults vqcCompress(VQCompressor *c, int quality);

3033
dreamcast/sfxlist.mk Normal file

File diff suppressed because it is too large Load Diff

23
dreamcast/sfxlooplist.mk Normal file
View File

@ -0,0 +1,23 @@
SFX_LOOP_WAV = \
sfx_0_loop.wav \
sfx_185_loop.wav \
sfx_19_loop.wav \
sfx_1_loop.wav \
sfx_2_loop.wav \
sfx_311_loop.wav \
sfx_331_loop.wav \
sfx_346_loop.wav \
sfx_349_loop.wav \
sfx_352_loop.wav \
sfx_355_loop.wav \
sfx_358_loop.wav \
sfx_361_loop.wav \
sfx_364_loop.wav \
sfx_367_loop.wav \
sfx_3_loop.wav \
sfx_438_loop.wav \
sfx_4_loop.wav \
sfx_5_loop.wav \
sfx_6_loop.wav \
sfx_7_loop.wav \
sfx_82_loop.wav

67
dreamcast/sim.mk Normal file
View File

@ -0,0 +1,67 @@
TARGET ?= dca3-sim.elf
IS_MAC := $(shell uname -s | grep -i "darwin" > /dev/null && echo "yes" || echo "no")
include common.mk
OBJS = $(RE3_OBJS) $(RW_OBJS) \
../src/audio/sampman_null.o
OBJS_SIM=$(OBJS:.o=.sim.o) \
../vendor/koshle/hlekos.sim.o \
../vendor/koshle/hlematrix3d.sim.o \
../vendor/koshle/hlepvr_mem.sim.o \
../vendor/koshle/hlepvr_prim.sim.o \
../vendor/koshle/hlepvr_scene.sim.o \
../vendor/koshle/hlepvr_misc.sim.o \
../vendor/koshle/hlepvr_init_term.sim.o \
../vendor/koshle/hlepvr_buffers.sim.o \
../vendor/koshle/hlepvr_irq.sim.o \
../vendor/koshle/hlepvr_fog.sim.o \
\
../vendor/emu/emu/window.sim.o \
\
../vendor/emu/lxdream/tacore.sim.o3 \
\
../vendor/emu/refsw/pvr_mem.sim.o3 \
../vendor/emu/refsw/pvr_regs.sim.o3 \
../vendor/emu/refsw/refsw_lists.sim.o3 \
../vendor/emu/refsw/refsw_tile.sim.o3 \
../vendor/emu/refsw/TexUtils.sim.o3 \
DEPS_SIM1=$(OBJS_SIM:.o=.d)
DEPS_SIM=$(DEPS_SIM1:.o3=.d)
CXXFLAGS+= -MMD -MP
ifeq ($(IS_MAC), yes)
%.sim.o: %.c
$(CC) -c -O0 -g -fno-pic -no-pie -o $@ $(CFLAGS) -I../vendor/koshle -I../vendor/emu -U_WIN32 -UWIN32 -UWINNT -Ui386 -DDC_SIM -D_FILE_OFFSET_BITS=64 -DMACOS64 $<
%.sim.o: %.cpp
$(CXX) -c -O0 -g -fno-pic -no-pie -o $@ $(CXXFLAGS) -I../vendor/koshle -I../vendor/emu -U_WIN32 -UWIN32 -UWINNT -Ui386 -DDC_SIM -D_FILE_OFFSET_BITS=64 -DMACOS64 $<
%.sim.o3: %.cpp
$(CXX) -c -O3 -g -fno-pic -no-pie -o $@ $(CXXFLAGS) -I../vendor/koshle -I../vendor/emu -U_WIN32 -UWIN32 -UWINNT -Ui386 -DDC_SIM -D_FILE_OFFSET_BITS=64 -DMACOS64 $<
else
# Using sse2 here for valgrind compatibility
%.sim.o: %.c
$(CC) -msse2 -mfpmath=sse -c -O0 -g -fno-pic -no-pie -o $@ $(CFLAGS) -I../vendor/koshle -I../vendor/emu -m32 -U_WIN32 -UWIN32 -UWINNT -Ui386 -DDC_SIM -D_FILE_OFFSET_BITS=64 $<
%.sim.o: %.cpp
$(CXX) -msse2 -mfpmath=sse -c -O0 -g -fno-pic -no-pie -o $@ $(CXXFLAGS) -I../vendor/koshle -I../vendor/emu -m32 -U_WIN32 -UWIN32 -UWINNT -Ui386 -DDC_SIM -D_FILE_OFFSET_BITS=64 $<
%.sim.o3: %.cpp
$(CXX) -msse2 -mfpmath=sse -c -O3 -g -fno-pic -no-pie -o $@ $(CXXFLAGS) -I../vendor/koshle -I../vendor/emu -m32 -U_WIN32 -UWIN32 -UWINNT -Ui386 -DDC_SIM -D_FILE_OFFSET_BITS=64 $<
endif
all: $(TARGET)
clean:
-rm -f $(OBJS_SIM) $(TARGET)
ifeq ($(IS_MAC), yes)
$(TARGET): $(OBJS_SIM)
$(CXX) -fno-pic -no-pie -o $(TARGET) $(OBJS_SIM) -lX11
else
$(TARGET): $(OBJS_SIM)
$(CXX) -m32 -fno-pic -no-pie -o $(TARGET) $(OBJS_SIM) -lX11
endif
-include $(DEPS_SIM)

Some files were not shown because too many files have changed in this diff Show More