mirror of
https://github.com/giongto35/cloud-game.git
synced 2026-08-02 06:43:21 +00:00
Add the initial libyuv support
The main benefit of libyuv, apart from shortening the video pipeline, is quite noticeable latency and CPU usage decrease due to various assembler/SIMD optimizations of the library. However, there is a drawback for macOS systems: libyuv cannot be downloaded as a compiled library and can only be built from the source, which means we should include a cropped source code of the library (~10K LoC) into the app or rise the complexity of macOS dev and run toolchains. The main target system -- Linux, and Windows will use compiled lib from the package managers and macOS will use the lib included as a shortened source-code. Building the app with the no_libyuv tag will force it to use libyuv from the provided source files.
This commit is contained in:
parent
072b674fb1
commit
b1b33713d6
73 changed files with 12017 additions and 1543 deletions
29
.github/workflows/build.yml
vendored
29
.github/workflows/build.yml
vendored
|
|
@ -1,5 +1,5 @@
|
|||
# ------------------------------------------------------------
|
||||
# Build workflow (Linux x64, macOS x64, Windows x64)
|
||||
# Build and test workflow (Linux x64, macOS x64, Windows x64)
|
||||
# ------------------------------------------------------------
|
||||
|
||||
name: build
|
||||
|
|
@ -20,7 +20,7 @@ jobs:
|
|||
strategy:
|
||||
matrix:
|
||||
os: [ ubuntu-latest, macos-latest, windows-latest ]
|
||||
step: [ build, check ]
|
||||
step: [ build, test ]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
|
@ -33,7 +33,7 @@ jobs:
|
|||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
sudo apt-get -qq update
|
||||
sudo apt-get -qq install -y make pkg-config libvpx-dev libx264-dev libopus-dev libsdl2-dev libgl1-mesa-glx
|
||||
sudo apt-get -qq install -y make pkg-config libvpx-dev libx264-dev libopus-dev libsdl2-dev libyuv-dev libgl1-mesa-glx
|
||||
|
||||
- name: Get MacOS dev libraries and tools
|
||||
if: matrix.os == 'macos-latest'
|
||||
|
|
@ -55,9 +55,10 @@ jobs:
|
|||
mingw-w64-x86_64-opus
|
||||
mingw-w64-x86_64-x264-git
|
||||
mingw-w64-x86_64-SDL2
|
||||
mingw-w64-x86_64-libyuv
|
||||
|
||||
- name: Get Windows OpenGL drivers
|
||||
if: matrix.step == 'check' && matrix.os == 'windows-latest'
|
||||
if: matrix.step == 'test' && matrix.os == 'windows-latest'
|
||||
shell: msys2 {0}
|
||||
run: |
|
||||
wget -q https://github.com/pal1000/mesa-dist-win/releases/download/20.2.1/mesa3d-20.2.1-release-mingw.7z
|
||||
|
|
@ -81,28 +82,28 @@ jobs:
|
|||
run: |
|
||||
make build
|
||||
|
||||
- name: Verify core rendering (windows-latest)
|
||||
if: matrix.step == 'check' && matrix.os == 'windows-latest' && always()
|
||||
- name: Test (windows-latest)
|
||||
if: matrix.step == 'test' && matrix.os == 'windows-latest' && always()
|
||||
shell: msys2 {0}
|
||||
env:
|
||||
MESA_GL_VERSION_OVERRIDE: 3.3COMPAT
|
||||
run: |
|
||||
GL_CTX=-autoGlContext make verify-cores
|
||||
GL_CTX=-autoGlContext make test verify-cores
|
||||
|
||||
- name: Verify core rendering (ubuntu-latest)
|
||||
if: matrix.step == 'check' && matrix.os == 'ubuntu-latest' && always()
|
||||
- name: Test (ubuntu-latest)
|
||||
if: matrix.step == 'test' && matrix.os == 'ubuntu-latest' && always()
|
||||
env:
|
||||
MESA_GL_VERSION_OVERRIDE: 3.3COMPAT
|
||||
run: |
|
||||
GL_CTX=-autoGlContext xvfb-run --auto-servernum make verify-cores
|
||||
GL_CTX=-autoGlContext xvfb-run --auto-servernum make test verify-cores
|
||||
|
||||
- name: Verify core rendering (macos-latest)
|
||||
if: matrix.step == 'check' && matrix.os == 'macos-latest' && always()
|
||||
- name: Test (macos-latest)
|
||||
if: matrix.step == 'test' && matrix.os == 'macos-latest' && always()
|
||||
run: |
|
||||
make verify-cores
|
||||
make test verify-cores
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: matrix.step == 'check' && always()
|
||||
if: matrix.step == 'test' && always()
|
||||
with:
|
||||
name: emulator-test-frames
|
||||
path: _rendered/*.png
|
||||
|
|
|
|||
|
|
@ -24,14 +24,16 @@ worker:
|
|||
domain: cloudretro.io
|
||||
|
||||
emulator:
|
||||
threads: 4
|
||||
libretro:
|
||||
logLevel: 1
|
||||
cores:
|
||||
list:
|
||||
mame:
|
||||
options:
|
||||
"fbneo-cpu-speed-adjust": "200%"
|
||||
"fbneo-diagnostic-input": "Hold Start"
|
||||
nes:
|
||||
scale: 2
|
||||
pcsx:
|
||||
altRepo: true
|
||||
snes:
|
||||
scale: 2
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ RUN apt-get -q update && apt-get -q install --no-install-recommends -y \
|
|||
libopus-dev \
|
||||
libsdl2-dev \
|
||||
libvpx-dev \
|
||||
libyuv-dev \
|
||||
libx264-dev \
|
||||
pkg-config \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
|
|
|||
5
Makefile
5
Makefile
|
|
@ -6,6 +6,8 @@ CGO_CFLAGS='-g -O3 -funroll-loops'
|
|||
CGO_LDFLAGS='-g -O3'
|
||||
GO_TAGS=static
|
||||
|
||||
.PHONY: clean test
|
||||
|
||||
fmt:
|
||||
@goimports -w cmd pkg tests
|
||||
@gofmt -s -w cmd pkg tests
|
||||
|
|
@ -32,6 +34,9 @@ build.worker:
|
|||
|
||||
build: build.coordinator build.worker
|
||||
|
||||
test:
|
||||
go test -v ./pkg/...
|
||||
|
||||
verify-cores:
|
||||
go test -run TestAll ./pkg/worker/room -v -renderFrames $(GL_CTX) -outputPath "../../../_rendered"
|
||||
|
||||
|
|
|
|||
|
|
@ -61,13 +61,13 @@ a better sense of performance.
|
|||
|
||||
```
|
||||
# Ubuntu / Windows (WSL2)
|
||||
apt-get install -y make gcc pkg-config libvpx-dev libx264-dev libopus-dev libsdl2-dev
|
||||
apt-get install -y make gcc pkg-config libvpx-dev libx264-dev libopus-dev libsdl2-dev libyuv-dev
|
||||
|
||||
# MacOS
|
||||
brew install pkg-config libvpx x264 opus sdl2
|
||||
|
||||
# Windows (MSYS2)
|
||||
pacman -Sy --noconfirm --needed git make mingw-w64-x86_64-{gcc,pkgconf,dlfcn,libvpx,opus,x264-git,SDL2}
|
||||
pacman -Sy --noconfirm --needed git make mingw-w64-x86_64-{gcc,pkgconf,dlfcn,libvpx,opus,x264-git,SDL2,libyuv}
|
||||
```
|
||||
|
||||
Because the coordinator and workers need to run simultaneously. Workers connect to the coordinator.
|
||||
|
|
|
|||
|
|
@ -99,12 +99,9 @@ worker:
|
|||
tag:
|
||||
|
||||
emulator:
|
||||
# set output viewport scale factor
|
||||
scale: 1
|
||||
|
||||
# set the total number of threads for the image processing
|
||||
# (experimental)
|
||||
threads: 4
|
||||
# (removed)
|
||||
threads: 0
|
||||
|
||||
aspectRatio:
|
||||
# enable aspect ratio changing
|
||||
|
|
@ -163,6 +160,7 @@ emulator:
|
|||
# - altRepo (bool) prioritize secondary repo as the download source
|
||||
# - lib (string)
|
||||
# - roms ([]string)
|
||||
# - scale (int) scales the output video frames by this factor.
|
||||
# - folder (string)
|
||||
# By default emulator selection is based on the folder named as cores
|
||||
# in the list (i.e. nes, snes) but if you specify folder param,
|
||||
|
|
@ -244,8 +242,6 @@ encoder:
|
|||
video:
|
||||
# h264, vpx (VP8)
|
||||
codec: h264
|
||||
# concurrent execution units (0 - disabled)
|
||||
concurrency: 0
|
||||
# see: https://trac.ffmpeg.org/wiki/Encode/H.264
|
||||
h264:
|
||||
# Constant Rate Factor (CRF) 0-51 (default: 23)
|
||||
|
|
@ -273,12 +269,6 @@ encoder:
|
|||
# one additional FFMPEG concat demux file
|
||||
recording:
|
||||
enabled: false
|
||||
# image compression level:
|
||||
# 0 - default compression
|
||||
# -1 - no compression
|
||||
# -2 - best speed
|
||||
# -3 - best compression
|
||||
compressLevel: 0
|
||||
# name contains the name of the recording dir (or zip)
|
||||
# format:
|
||||
# %date:go_time_format% -- refer: https://go.dev/src/time/format.go
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import (
|
|||
)
|
||||
|
||||
type Emulator struct {
|
||||
Scale int
|
||||
Threads int
|
||||
AspectRatio struct {
|
||||
Keep bool
|
||||
|
|
@ -54,6 +53,7 @@ type LibretroCoreConfig struct {
|
|||
Lib string
|
||||
Options map[string]string
|
||||
Roms []string
|
||||
Scale float64
|
||||
UsesLibCo bool
|
||||
VFR bool
|
||||
Width int
|
||||
|
|
|
|||
|
|
@ -41,11 +41,10 @@ type Server struct {
|
|||
}
|
||||
|
||||
type Recording struct {
|
||||
Enabled bool
|
||||
CompressLevel int
|
||||
Name string
|
||||
Folder string
|
||||
Zip bool
|
||||
Enabled bool
|
||||
Name string
|
||||
Folder string
|
||||
Zip bool
|
||||
}
|
||||
|
||||
func (s *Server) WithFlags() {
|
||||
|
|
|
|||
|
|
@ -52,9 +52,8 @@ type Audio struct {
|
|||
}
|
||||
|
||||
type Video struct {
|
||||
Codec string
|
||||
Concurrency int
|
||||
H264 struct {
|
||||
Codec string
|
||||
H264 struct {
|
||||
Crf uint8
|
||||
LogLevel int32
|
||||
Preset string
|
||||
|
|
|
|||
56
pkg/encoder/color/bgra/bgra.go
Normal file
56
pkg/encoder/color/bgra/bgra.go
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
package bgra
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
)
|
||||
|
||||
type BGRA struct {
|
||||
image.RGBA
|
||||
}
|
||||
|
||||
var BGRAModel = color.ModelFunc(func(c color.Color) color.Color {
|
||||
if _, ok := c.(BGRAColor); ok {
|
||||
return c
|
||||
}
|
||||
r, g, b, a := c.RGBA()
|
||||
return BGRAColor{uint8(r >> 8), uint8(g >> 8), uint8(b >> 8), uint8(a >> 8)}
|
||||
})
|
||||
|
||||
// BGRAColor represents a BGRA color.
|
||||
type BGRAColor struct {
|
||||
R, G, B, A uint8
|
||||
}
|
||||
|
||||
func (c BGRAColor) RGBA() (r, g, b, a uint32) {
|
||||
r = uint32(c.B)
|
||||
r |= r << 8
|
||||
g = uint32(c.G)
|
||||
g |= g << 8
|
||||
b = uint32(c.R)
|
||||
b |= b << 8
|
||||
a = uint32(255) //uint32(c.A)
|
||||
a |= a << 8
|
||||
return
|
||||
}
|
||||
|
||||
func NewBGRA(r image.Rectangle) *BGRA {
|
||||
return &BGRA{*image.NewRGBA(r)}
|
||||
}
|
||||
|
||||
func (p *BGRA) ColorModel() color.Model { return BGRAModel }
|
||||
func (p *BGRA) At(x, y int) color.Color {
|
||||
i := p.PixOffset(x, y)
|
||||
s := p.Pix[i : i+4 : i+4]
|
||||
return BGRAColor{s[0], s[1], s[2], s[3]}
|
||||
}
|
||||
|
||||
func (p *BGRA) Set(x, y int, c color.Color) {
|
||||
i := p.PixOffset(x, y)
|
||||
c1 := BGRAModel.Convert(c).(BGRAColor)
|
||||
s := p.Pix[i : i+4 : i+4]
|
||||
s[0] = c1.R
|
||||
s[1] = c1.G
|
||||
s[2] = c1.B
|
||||
s[3] = 255
|
||||
}
|
||||
62
pkg/encoder/color/rgb565/rgb565.go
Normal file
62
pkg/encoder/color/rgb565/rgb565.go
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
package rgb565
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"image"
|
||||
"image/color"
|
||||
"math"
|
||||
)
|
||||
|
||||
// RGB565 is an in-memory image whose At method returns RGB565 values.
|
||||
type RGB565 struct {
|
||||
// Pix holds the image's pixels, as RGB565 values in big-endian format. The pixel at
|
||||
// (x, y) starts at Pix[(y-p.Rect.Min.Y)*p.Stride + (x-p.Rect.Min.X)*2].
|
||||
Pix []uint8
|
||||
// Stride is the Pix stride (in bytes) between vertically adjacent pixels.
|
||||
Stride int
|
||||
// Rect is the image's bounds.
|
||||
Rect image.Rectangle
|
||||
}
|
||||
|
||||
// Model is the model for RGB565 colors.
|
||||
var Model = color.ModelFunc(func(c color.Color) color.Color {
|
||||
//if _, ok := c.(Color); ok {
|
||||
// return c
|
||||
//}
|
||||
r, g, b, _ := c.RGBA()
|
||||
return Color(uint16((r<<8)&rMask | (g<<3)&gMask | (b>>3)&bMask))
|
||||
})
|
||||
|
||||
const (
|
||||
rMask = 0b1111100000000000
|
||||
gMask = 0b0000011111100000
|
||||
bMask = 0b0000000000011111
|
||||
)
|
||||
|
||||
// Color represents an RGB565 color.
|
||||
type Color uint16
|
||||
|
||||
func (c Color) RGBA() (r, g, b, a uint32) {
|
||||
return uint32(math.Round(float64(c&rMask>>11)*255.0/31.0)) << 8,
|
||||
uint32(math.Round(float64(c&gMask>>5)*255.0/63.0)) << 8,
|
||||
uint32(math.Round(float64(c&bMask)*255.0/31.0)) << 8,
|
||||
0xffff
|
||||
}
|
||||
|
||||
func NewRGB565(r image.Rectangle) *RGB565 {
|
||||
return &RGB565{Pix: make([]uint8, r.Dx()*r.Dy()<<1), Stride: r.Dx() << 1, Rect: r}
|
||||
}
|
||||
|
||||
func (p *RGB565) Bounds() image.Rectangle { return p.Rect }
|
||||
func (p *RGB565) ColorModel() color.Model { return Model }
|
||||
func (p *RGB565) PixOffset(x, y int) int { return (x-p.Rect.Min.X)<<1 + (y-p.Rect.Min.Y)*p.Stride }
|
||||
|
||||
func (p *RGB565) At(x, y int) color.Color {
|
||||
i := p.PixOffset(x, y)
|
||||
return Color(binary.LittleEndian.Uint16(p.Pix[i : i+2]))
|
||||
}
|
||||
|
||||
func (p *RGB565) Set(x, y int, c color.Color) {
|
||||
i := p.PixOffset(x, y)
|
||||
binary.LittleEndian.PutUint16(p.Pix[i:i+2], uint16(Model.Convert(c).(Color)))
|
||||
}
|
||||
24
pkg/encoder/color/rgba/rgba.go
Normal file
24
pkg/encoder/color/rgba/rgba.go
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
package rgba
|
||||
|
||||
import (
|
||||
"image"
|
||||
"image/color"
|
||||
)
|
||||
|
||||
func ToRGBA(img image.Image, flipped bool) *image.RGBA {
|
||||
bounds := img.Bounds()
|
||||
sw, sh := bounds.Dx(), bounds.Dy()
|
||||
dst := image.NewRGBA(image.Rect(0, 0, sw, sh))
|
||||
for y := 0; y < sh; y++ {
|
||||
yy := y
|
||||
if flipped {
|
||||
yy = sh - y
|
||||
}
|
||||
for x := 0; x < sw; x++ {
|
||||
px := img.At(x, y)
|
||||
rgba := color.RGBAModel.Convert(px).(color.RGBA)
|
||||
dst.Set(x, yy, rgba)
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package encoder
|
||||
|
||||
import (
|
||||
"image"
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
|
|
@ -10,7 +10,7 @@ import (
|
|||
)
|
||||
|
||||
type (
|
||||
InFrame *image.RGBA
|
||||
InFrame yuv.RawFrame
|
||||
OutFrame []byte
|
||||
Encoder interface {
|
||||
LoadBuf(input []byte)
|
||||
|
|
@ -21,11 +21,13 @@ type (
|
|||
}
|
||||
)
|
||||
|
||||
type VideoEncoder struct {
|
||||
encoder Encoder
|
||||
type Video struct {
|
||||
codec Encoder
|
||||
log *logger.Logger
|
||||
stopped atomic.Bool
|
||||
y yuv.ImgProcessor
|
||||
y yuv.Conv
|
||||
pf yuv.PixFmt
|
||||
rot uint
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
|
|
@ -41,39 +43,63 @@ const (
|
|||
// converts them into YUV I420 format,
|
||||
// encodes with provided video encoder, and
|
||||
// puts the result into the output channel.
|
||||
func NewVideoEncoder(enc Encoder, w, h int, concurrency int, log *logger.Logger) *VideoEncoder {
|
||||
y := yuv.NewYuvImgProcessor(w, h, &yuv.Options{Threads: concurrency})
|
||||
if concurrency > 0 {
|
||||
log.Info().Msgf("Use concurrent image processor: %v", concurrency)
|
||||
}
|
||||
return &VideoEncoder{encoder: enc, y: y, log: log}
|
||||
func NewVideoEncoder(codec Encoder, w, h int, scale float64, log *logger.Logger) *Video {
|
||||
return &Video{codec: codec, y: yuv.NewYuvConv(w, h, scale), log: log}
|
||||
}
|
||||
|
||||
func (vp *VideoEncoder) Encode(img InFrame) OutFrame {
|
||||
vp.mu.Lock()
|
||||
defer vp.mu.Unlock()
|
||||
if vp.stopped.Load() {
|
||||
func (v *Video) Encode(frame InFrame) OutFrame {
|
||||
v.mu.Lock()
|
||||
defer v.mu.Unlock()
|
||||
if v.stopped.Load() {
|
||||
return nil
|
||||
}
|
||||
|
||||
yCbCr := vp.y.Process(img)
|
||||
vp.encoder.LoadBuf(yCbCr)
|
||||
vp.y.Put(&yCbCr)
|
||||
yCbCr := v.y.Process(yuv.RawFrame(frame), v.rot, v.pf)
|
||||
v.codec.LoadBuf(yCbCr)
|
||||
v.y.Put(&yCbCr)
|
||||
|
||||
if frame := vp.encoder.Encode(); len(frame) > 0 {
|
||||
return frame
|
||||
if bytes := v.codec.Encode(); len(bytes) > 0 {
|
||||
return bytes
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vp *VideoEncoder) SetFlip(b bool) { vp.encoder.SetFlip(b) }
|
||||
func (v *Video) Info() string { return fmt.Sprintf("libyuv: %v", v.y.Version()) }
|
||||
|
||||
func (vp *VideoEncoder) Stop() {
|
||||
vp.stopped.Store(true)
|
||||
vp.mu.Lock()
|
||||
defer vp.mu.Unlock()
|
||||
|
||||
if err := vp.encoder.Shutdown(); err != nil {
|
||||
vp.log.Error().Err(err).Msg("failed to close the encoder")
|
||||
func (v *Video) SetPixFormat(f uint32) {
|
||||
switch f {
|
||||
case 1:
|
||||
v.pf = yuv.PixFmt(yuv.FourccArgb)
|
||||
case 2:
|
||||
v.pf = yuv.PixFmt(yuv.FourccRgbp)
|
||||
default:
|
||||
v.pf = yuv.PixFmt(yuv.FourccAbgr)
|
||||
}
|
||||
}
|
||||
|
||||
// SetRot sets the rotation angle of the frames.
|
||||
func (v *Video) SetRot(r uint) {
|
||||
switch r {
|
||||
// de-rotate
|
||||
case 90:
|
||||
v.rot = 270
|
||||
case 270:
|
||||
v.rot = 90
|
||||
default:
|
||||
v.rot = r
|
||||
}
|
||||
}
|
||||
|
||||
// SetFlip tells the encoder to flip the frames vertically.
|
||||
func (v *Video) SetFlip(b bool) { v.codec.SetFlip(b) }
|
||||
|
||||
func (v *Video) Stop() {
|
||||
v.stopped.Store(true)
|
||||
v.mu.Lock()
|
||||
defer v.mu.Unlock()
|
||||
v.rot = 0
|
||||
|
||||
if err := v.codec.Shutdown(); err != nil {
|
||||
v.log.Error().Err(err).Msg("failed to close the encoder")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
29
pkg/encoder/yuv/libyuv/LICENSE
Normal file
29
pkg/encoder/yuv/libyuv/LICENSE
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
Copyright 2011 The LibYuv Project Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the name of Google nor the names of its contributors may
|
||||
be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
29
pkg/encoder/yuv/libyuv/basic_types.h
Normal file
29
pkg/encoder/yuv/libyuv/basic_types.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright 2011 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_LIBYUV_BASIC_TYPES_H_
|
||||
#define INCLUDE_LIBYUV_BASIC_TYPES_H_
|
||||
|
||||
#include <stddef.h> // For size_t and NULL
|
||||
|
||||
#if !defined(INT_TYPES_DEFINED) && !defined(GG_LONGLONG)
|
||||
#define INT_TYPES_DEFINED
|
||||
|
||||
#include <stdint.h> // for uintptr_t and C99 types
|
||||
|
||||
#endif // INT_TYPES_DEFINED
|
||||
|
||||
#if !defined(LIBYUV_API)
|
||||
#define LIBYUV_API
|
||||
#endif // LIBYUV_API
|
||||
|
||||
#define LIBYUV_BOOL int
|
||||
|
||||
#endif // INCLUDE_LIBYUV_BASIC_TYPES_H_
|
||||
336
pkg/encoder/yuv/libyuv/convert.c
Normal file
336
pkg/encoder/yuv/libyuv/convert.c
Normal file
|
|
@ -0,0 +1,336 @@
|
|||
/*
|
||||
* Copyright 2011 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "convert.h"
|
||||
|
||||
#include "basic_types.h"
|
||||
#include "cpu_id.h"
|
||||
#include "planar_functions.h"
|
||||
#include "row.h"
|
||||
|
||||
// Subsample amount uses a shift.
|
||||
// v is value
|
||||
// a is amount to add to round up
|
||||
// s is shift to subsample down
|
||||
#define SUBSAMPLE(v, a, s) (v < 0) ? (-((-v + a) >> s)) : ((v + a) >> s)
|
||||
|
||||
static __inline int Abs(int v) {
|
||||
return v >= 0 ? v : -v;
|
||||
}
|
||||
|
||||
// Copy I420 with optional flipping.
|
||||
// TODO(fbarchard): Use Scale plane which supports mirroring, but ensure
|
||||
// is does row coalescing.
|
||||
LIBYUV_API
|
||||
int I420Copy(const uint8_t *src_y,
|
||||
int src_stride_y,
|
||||
const uint8_t *src_u,
|
||||
int src_stride_u,
|
||||
const uint8_t *src_v,
|
||||
int src_stride_v,
|
||||
uint8_t *dst_y,
|
||||
int dst_stride_y,
|
||||
uint8_t *dst_u,
|
||||
int dst_stride_u,
|
||||
uint8_t *dst_v,
|
||||
int dst_stride_v,
|
||||
int width,
|
||||
int height) {
|
||||
int halfwidth = (width + 1) >> 1;
|
||||
int halfheight = (height + 1) >> 1;
|
||||
if ((!src_y && dst_y) || !src_u || !src_v || !dst_u || !dst_v || width <= 0 ||
|
||||
height == 0) {
|
||||
return -1;
|
||||
}
|
||||
// Negative height means invert the image.
|
||||
if (height < 0) {
|
||||
height = -height;
|
||||
halfheight = (height + 1) >> 1;
|
||||
src_y = src_y + (height - 1) * src_stride_y;
|
||||
src_u = src_u + (halfheight - 1) * src_stride_u;
|
||||
src_v = src_v + (halfheight - 1) * src_stride_v;
|
||||
src_stride_y = -src_stride_y;
|
||||
src_stride_u = -src_stride_u;
|
||||
src_stride_v = -src_stride_v;
|
||||
}
|
||||
|
||||
if (dst_y) {
|
||||
CopyPlane(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
|
||||
}
|
||||
// Copy UV planes.
|
||||
CopyPlane(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth, halfheight);
|
||||
CopyPlane(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth, halfheight);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Convert ARGB to I420.
|
||||
LIBYUV_API
|
||||
int ARGBToI420(const uint8_t *src_argb,
|
||||
int src_stride_argb,
|
||||
uint8_t *dst_y,
|
||||
int dst_stride_y,
|
||||
uint8_t *dst_u,
|
||||
int dst_stride_u,
|
||||
uint8_t *dst_v,
|
||||
int dst_stride_v,
|
||||
int width,
|
||||
int height) {
|
||||
int y;
|
||||
void (*ARGBToUVRow)(const uint8_t *src_argb0, int src_stride_argb,
|
||||
uint8_t *dst_u, uint8_t *dst_v, int width) =
|
||||
ARGBToUVRow_C;
|
||||
void (*ARGBToYRow)(const uint8_t *src_argb, uint8_t *dst_y, int width) =
|
||||
ARGBToYRow_C;
|
||||
if (!src_argb || !dst_y || !dst_u || !dst_v || width <= 0 || height == 0) {
|
||||
return -1;
|
||||
}
|
||||
// Negative height means invert the image.
|
||||
if (height < 0) {
|
||||
height = -height;
|
||||
src_argb = src_argb + (height - 1) * src_stride_argb;
|
||||
src_stride_argb = -src_stride_argb;
|
||||
}
|
||||
#if defined(HAS_ARGBTOYROW_SSSE3)
|
||||
if (TestCpuFlag(kCpuHasSSSE3)) {
|
||||
ARGBToYRow = ARGBToYRow_Any_SSSE3;
|
||||
if (IS_ALIGNED(width, 16)) {
|
||||
ARGBToYRow = ARGBToYRow_SSSE3;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(HAS_ARGBTOUVROW_SSSE3)
|
||||
if (TestCpuFlag(kCpuHasSSSE3)) {
|
||||
ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
|
||||
if (IS_ALIGNED(width, 16)) {
|
||||
ARGBToUVRow = ARGBToUVRow_SSSE3;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(HAS_ARGBTOYROW_AVX2)
|
||||
if (TestCpuFlag(kCpuHasAVX2)) {
|
||||
ARGBToYRow = ARGBToYRow_Any_AVX2;
|
||||
if (IS_ALIGNED(width, 32)) {
|
||||
ARGBToYRow = ARGBToYRow_AVX2;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(HAS_ARGBTOUVROW_AVX2)
|
||||
if (TestCpuFlag(kCpuHasAVX2)) {
|
||||
ARGBToUVRow = ARGBToUVRow_Any_AVX2;
|
||||
if (IS_ALIGNED(width, 32)) {
|
||||
ARGBToUVRow = ARGBToUVRow_AVX2;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
for (y = 0; y < height - 1; y += 2) {
|
||||
ARGBToUVRow(src_argb, src_stride_argb, dst_u, dst_v, width);
|
||||
ARGBToYRow(src_argb, dst_y, width);
|
||||
ARGBToYRow(src_argb + src_stride_argb, dst_y + dst_stride_y, width);
|
||||
src_argb += src_stride_argb * 2;
|
||||
dst_y += dst_stride_y * 2;
|
||||
dst_u += dst_stride_u;
|
||||
dst_v += dst_stride_v;
|
||||
}
|
||||
if (height & 1) {
|
||||
ARGBToUVRow(src_argb, 0, dst_u, dst_v, width);
|
||||
ARGBToYRow(src_argb, dst_y, width);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Convert ABGR to I420.
|
||||
LIBYUV_API
|
||||
int ABGRToI420(const uint8_t *src_abgr,
|
||||
int src_stride_abgr,
|
||||
uint8_t *dst_y,
|
||||
int dst_stride_y,
|
||||
uint8_t *dst_u,
|
||||
int dst_stride_u,
|
||||
uint8_t *dst_v,
|
||||
int dst_stride_v,
|
||||
int width,
|
||||
int height) {
|
||||
int y;
|
||||
void (*ABGRToUVRow)(const uint8_t *src_abgr0, int src_stride_abgr,
|
||||
uint8_t *dst_u, uint8_t *dst_v, int width) =
|
||||
ABGRToUVRow_C;
|
||||
void (*ABGRToYRow)(const uint8_t *src_abgr, uint8_t *dst_y, int width) =
|
||||
ABGRToYRow_C;
|
||||
if (!src_abgr || !dst_y || !dst_u || !dst_v || width <= 0 || height == 0) {
|
||||
return -1;
|
||||
}
|
||||
// Negative height means invert the image.
|
||||
if (height < 0) {
|
||||
height = -height;
|
||||
src_abgr = src_abgr + (height - 1) * src_stride_abgr;
|
||||
src_stride_abgr = -src_stride_abgr;
|
||||
}
|
||||
#if defined(HAS_ABGRTOYROW_SSSE3)
|
||||
if (TestCpuFlag(kCpuHasSSSE3)) {
|
||||
ABGRToYRow = ABGRToYRow_Any_SSSE3;
|
||||
if (IS_ALIGNED(width, 16)) {
|
||||
ABGRToYRow = ABGRToYRow_SSSE3;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(HAS_ABGRTOUVROW_SSSE3)
|
||||
if (TestCpuFlag(kCpuHasSSSE3)) {
|
||||
ABGRToUVRow = ABGRToUVRow_Any_SSSE3;
|
||||
if (IS_ALIGNED(width, 16)) {
|
||||
ABGRToUVRow = ABGRToUVRow_SSSE3;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(HAS_ABGRTOYROW_AVX2)
|
||||
if (TestCpuFlag(kCpuHasAVX2)) {
|
||||
ABGRToYRow = ABGRToYRow_Any_AVX2;
|
||||
if (IS_ALIGNED(width, 32)) {
|
||||
ABGRToYRow = ABGRToYRow_AVX2;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(HAS_ABGRTOUVROW_AVX2)
|
||||
if (TestCpuFlag(kCpuHasAVX2)) {
|
||||
ABGRToUVRow = ABGRToUVRow_Any_AVX2;
|
||||
if (IS_ALIGNED(width, 32)) {
|
||||
ABGRToUVRow = ABGRToUVRow_AVX2;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
for (y = 0; y < height - 1; y += 2) {
|
||||
ABGRToUVRow(src_abgr, src_stride_abgr, dst_u, dst_v, width);
|
||||
ABGRToYRow(src_abgr, dst_y, width);
|
||||
ABGRToYRow(src_abgr + src_stride_abgr, dst_y + dst_stride_y, width);
|
||||
src_abgr += src_stride_abgr * 2;
|
||||
dst_y += dst_stride_y * 2;
|
||||
dst_u += dst_stride_u;
|
||||
dst_v += dst_stride_v;
|
||||
}
|
||||
if (height & 1) {
|
||||
ABGRToUVRow(src_abgr, 0, dst_u, dst_v, width);
|
||||
ABGRToYRow(src_abgr, dst_y, width);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Convert RGB565 to I420.
|
||||
LIBYUV_API
|
||||
int RGB565ToI420(const uint8_t *src_rgb565,
|
||||
int src_stride_rgb565,
|
||||
uint8_t *dst_y,
|
||||
int dst_stride_y,
|
||||
uint8_t *dst_u,
|
||||
int dst_stride_u,
|
||||
uint8_t *dst_v,
|
||||
int dst_stride_v,
|
||||
int width,
|
||||
int height) {
|
||||
int y;
|
||||
void (*RGB565ToARGBRow)(const uint8_t *src_rgb, uint8_t *dst_argb,
|
||||
int width) = RGB565ToARGBRow_C;
|
||||
void (*ARGBToUVRow)(const uint8_t *src_argb0, int src_stride_argb,
|
||||
uint8_t *dst_u, uint8_t *dst_v, int width) =
|
||||
ARGBToUVRow_C;
|
||||
void (*ARGBToYRow)(const uint8_t *src_argb, uint8_t *dst_y, int width) =
|
||||
ARGBToYRow_C;
|
||||
if (!src_rgb565 || !dst_y || !dst_u || !dst_v || width <= 0 || height == 0) {
|
||||
return -1;
|
||||
}
|
||||
// Negative height means invert the image.
|
||||
if (height < 0) {
|
||||
height = -height;
|
||||
src_rgb565 = src_rgb565 + (height - 1) * src_stride_rgb565;
|
||||
src_stride_rgb565 = -src_stride_rgb565;
|
||||
}
|
||||
|
||||
#if defined(HAS_RGB565TOARGBROW_SSE2)
|
||||
if (TestCpuFlag(kCpuHasSSE2)) {
|
||||
RGB565ToARGBRow = RGB565ToARGBRow_Any_SSE2;
|
||||
if (IS_ALIGNED(width, 8)) {
|
||||
RGB565ToARGBRow = RGB565ToARGBRow_SSE2;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(HAS_RGB565TOARGBROW_AVX2)
|
||||
if (TestCpuFlag(kCpuHasAVX2)) {
|
||||
RGB565ToARGBRow = RGB565ToARGBRow_Any_AVX2;
|
||||
if (IS_ALIGNED(width, 16)) {
|
||||
RGB565ToARGBRow = RGB565ToARGBRow_AVX2;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(HAS_ARGBTOYROW_SSSE3)
|
||||
if (TestCpuFlag(kCpuHasSSSE3)) {
|
||||
ARGBToYRow = ARGBToYRow_Any_SSSE3;
|
||||
if (IS_ALIGNED(width, 16)) {
|
||||
ARGBToYRow = ARGBToYRow_SSSE3;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(HAS_ARGBTOUVROW_SSSE3)
|
||||
if (TestCpuFlag(kCpuHasSSSE3)) {
|
||||
ARGBToUVRow = ARGBToUVRow_Any_SSSE3;
|
||||
if (IS_ALIGNED(width, 16)) {
|
||||
ARGBToUVRow = ARGBToUVRow_SSSE3;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(HAS_ARGBTOYROW_AVX2)
|
||||
if (TestCpuFlag(kCpuHasAVX2)) {
|
||||
ARGBToYRow = ARGBToYRow_Any_AVX2;
|
||||
if (IS_ALIGNED(width, 32)) {
|
||||
ARGBToYRow = ARGBToYRow_AVX2;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(HAS_ARGBTOUVROW_AVX2)
|
||||
if (TestCpuFlag(kCpuHasAVX2)) {
|
||||
ARGBToUVRow = ARGBToUVRow_Any_AVX2;
|
||||
if (IS_ALIGNED(width, 32)) {
|
||||
ARGBToUVRow = ARGBToUVRow_AVX2;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
{
|
||||
#if !(defined(HAS_RGB565TOYROW_NEON))
|
||||
// Allocate 2 rows of ARGB.
|
||||
const int row_size = (width * 4 + 31) & ~31;
|
||||
align_buffer_64(row, row_size * 2);
|
||||
#endif
|
||||
for (y = 0; y < height - 1; y += 2) {
|
||||
#if (defined(HAS_RGB565TOYROW_NEON))
|
||||
#else
|
||||
RGB565ToARGBRow(src_rgb565, row, width);
|
||||
RGB565ToARGBRow(src_rgb565 + src_stride_rgb565, row + row_size, width);
|
||||
ARGBToUVRow(row, row_size, dst_u, dst_v, width);
|
||||
ARGBToYRow(row, dst_y, width);
|
||||
ARGBToYRow(row + row_size, dst_y + dst_stride_y, width);
|
||||
#endif
|
||||
src_rgb565 += src_stride_rgb565 * 2;
|
||||
dst_y += dst_stride_y * 2;
|
||||
dst_u += dst_stride_u;
|
||||
dst_v += dst_stride_v;
|
||||
}
|
||||
if (height & 1) {
|
||||
#if (defined(HAS_RGB565TOYROW_NEON))
|
||||
#else
|
||||
RGB565ToARGBRow(src_rgb565, row, width);
|
||||
ARGBToUVRow(row, 0, dst_u, dst_v, width);
|
||||
ARGBToYRow(row, dst_y, width);
|
||||
#endif
|
||||
}
|
||||
#if !(defined(HAS_RGB565TOYROW_NEON))
|
||||
free_aligned_buffer_64(row);
|
||||
#endif
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
113
pkg/encoder/yuv/libyuv/convert.h
Normal file
113
pkg/encoder/yuv/libyuv/convert.h
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Copyright 2011 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_LIBYUV_CONVERT_H_
|
||||
#define INCLUDE_LIBYUV_CONVERT_H_
|
||||
|
||||
#include "rotate.h" // For enum RotationMode.
|
||||
|
||||
// Copy I420 to I420.
|
||||
#define I420ToI420 I420Copy
|
||||
LIBYUV_API
|
||||
int I420Copy(const uint8_t *src_y,
|
||||
int src_stride_y,
|
||||
const uint8_t *src_u,
|
||||
int src_stride_u,
|
||||
const uint8_t *src_v,
|
||||
int src_stride_v,
|
||||
uint8_t *dst_y,
|
||||
int dst_stride_y,
|
||||
uint8_t *dst_u,
|
||||
int dst_stride_u,
|
||||
uint8_t *dst_v,
|
||||
int dst_stride_v,
|
||||
int width,
|
||||
int height);
|
||||
|
||||
// ARGB little endian (bgra in memory) to I420.
|
||||
LIBYUV_API
|
||||
int ARGBToI420(const uint8_t *src_argb,
|
||||
int src_stride_argb,
|
||||
uint8_t *dst_y,
|
||||
int dst_stride_y,
|
||||
uint8_t *dst_u,
|
||||
int dst_stride_u,
|
||||
uint8_t *dst_v,
|
||||
int dst_stride_v,
|
||||
int width,
|
||||
int height);
|
||||
|
||||
// ABGR little endian (rgba in memory) to I420.
|
||||
LIBYUV_API
|
||||
int ABGRToI420(const uint8_t *src_abgr,
|
||||
int src_stride_abgr,
|
||||
uint8_t *dst_y,
|
||||
int dst_stride_y,
|
||||
uint8_t *dst_u,
|
||||
int dst_stride_u,
|
||||
uint8_t *dst_v,
|
||||
int dst_stride_v,
|
||||
int width,
|
||||
int height);
|
||||
|
||||
// RGB16 (RGBP fourcc) little endian to I420.
|
||||
LIBYUV_API
|
||||
int RGB565ToI420(const uint8_t *src_rgb565,
|
||||
int src_stride_rgb565,
|
||||
uint8_t *dst_y,
|
||||
int dst_stride_y,
|
||||
uint8_t *dst_u,
|
||||
int dst_stride_u,
|
||||
uint8_t *dst_v,
|
||||
int dst_stride_v,
|
||||
int width,
|
||||
int height);
|
||||
|
||||
// Convert camera sample to I420 with cropping, rotation and vertical flip.
|
||||
// "src_size" is needed to parse MJPG.
|
||||
// "dst_stride_y" number of bytes in a row of the dst_y plane.
|
||||
// Normally this would be the same as dst_width, with recommended alignment
|
||||
// to 16 bytes for better efficiency.
|
||||
// If rotation of 90 or 270 is used, stride is affected. The caller should
|
||||
// allocate the I420 buffer according to rotation.
|
||||
// "dst_stride_u" number of bytes in a row of the dst_u plane.
|
||||
// Normally this would be the same as (dst_width + 1) / 2, with
|
||||
// recommended alignment to 16 bytes for better efficiency.
|
||||
// If rotation of 90 or 270 is used, stride is affected.
|
||||
// "crop_x" and "crop_y" are starting position for cropping.
|
||||
// To center, crop_x = (src_width - dst_width) / 2
|
||||
// crop_y = (src_height - dst_height) / 2
|
||||
// "src_width" / "src_height" is size of src_frame in pixels.
|
||||
// "src_height" can be negative indicating a vertically flipped image source.
|
||||
// "crop_width" / "crop_height" is the size to crop the src to.
|
||||
// Must be less than or equal to src_width/src_height
|
||||
// Cropping parameters are pre-rotation.
|
||||
// "rotation" can be 0, 90, 180 or 270.
|
||||
// "fourcc" is a fourcc. ie 'I420', 'YUY2'
|
||||
// Returns 0 for successful; -1 for invalid parameter. Non-zero for failure.
|
||||
LIBYUV_API
|
||||
int ConvertToI420(const uint8_t *sample,
|
||||
size_t sample_size,
|
||||
uint8_t *dst_y,
|
||||
int dst_stride_y,
|
||||
uint8_t *dst_u,
|
||||
int dst_stride_u,
|
||||
uint8_t *dst_v,
|
||||
int dst_stride_v,
|
||||
int crop_x,
|
||||
int crop_y,
|
||||
int src_width,
|
||||
int src_height,
|
||||
int crop_width,
|
||||
int crop_height,
|
||||
enum RotationMode rotation,
|
||||
uint32_t fourcc);
|
||||
|
||||
#endif // INCLUDE_LIBYUV_CONVERT_H_
|
||||
24
pkg/encoder/yuv/libyuv/convert_argb.h
Normal file
24
pkg/encoder/yuv/libyuv/convert_argb.h
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright 2012 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_LIBYUV_CONVERT_ARGB_H_
|
||||
#define INCLUDE_LIBYUV_CONVERT_ARGB_H_
|
||||
|
||||
#include "basic_types.h"
|
||||
|
||||
// Conversion matrix for YVU to BGR
|
||||
LIBYUV_API extern const struct YuvConstants kYvuI601Constants; // BT.601
|
||||
LIBYUV_API extern const struct YuvConstants kYvuJPEGConstants; // BT.601 full
|
||||
LIBYUV_API extern const struct YuvConstants kYvuH709Constants; // BT.709
|
||||
LIBYUV_API extern const struct YuvConstants kYvuF709Constants; // BT.709 full
|
||||
LIBYUV_API extern const struct YuvConstants kYvu2020Constants; // BT.2020
|
||||
LIBYUV_API extern const struct YuvConstants kYvuV2020Constants; // BT.2020 full
|
||||
|
||||
#endif // INCLUDE_LIBYUV_CONVERT_ARGB_H_
|
||||
116
pkg/encoder/yuv/libyuv/convert_to_i420.c
Normal file
116
pkg/encoder/yuv/libyuv/convert_to_i420.c
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* Copyright 2011 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "convert.h"
|
||||
#include "video_common.h"
|
||||
|
||||
// Convert camera sample to I420 with cropping, rotation and vertical flip.
|
||||
// src_width is used for source stride computation
|
||||
// src_height is used to compute location of planes, and indicate inversion
|
||||
// sample_size is measured in bytes and is the size of the frame.
|
||||
// With MJPEG it is the compressed size of the frame.
|
||||
LIBYUV_API
|
||||
int ConvertToI420(const uint8_t *sample,
|
||||
size_t sample_size,
|
||||
uint8_t *dst_y,
|
||||
int dst_stride_y,
|
||||
uint8_t *dst_u,
|
||||
int dst_stride_u,
|
||||
uint8_t *dst_v,
|
||||
int dst_stride_v,
|
||||
int crop_x,
|
||||
int crop_y,
|
||||
int src_width,
|
||||
int src_height,
|
||||
int crop_width,
|
||||
int crop_height,
|
||||
enum RotationMode rotation,
|
||||
uint32_t fourcc) {
|
||||
uint32_t format = CanonicalFourCC(fourcc);
|
||||
const uint8_t *src;
|
||||
// TODO(nisse): Why allow crop_height < 0?
|
||||
const int abs_crop_height = (crop_height < 0) ? -crop_height : crop_height;
|
||||
int r = 0;
|
||||
LIBYUV_BOOL need_buf =
|
||||
(rotation && format != FOURCC_I420 && format != FOURCC_NV12 &&
|
||||
format != FOURCC_NV21 && format != FOURCC_YV12) ||
|
||||
dst_y == sample;
|
||||
uint8_t *tmp_y = dst_y;
|
||||
uint8_t *tmp_u = dst_u;
|
||||
uint8_t *tmp_v = dst_v;
|
||||
int tmp_y_stride = dst_stride_y;
|
||||
int tmp_u_stride = dst_stride_u;
|
||||
int tmp_v_stride = dst_stride_v;
|
||||
uint8_t *rotate_buffer = NULL;
|
||||
const int inv_crop_height =
|
||||
(src_height < 0) ? -abs_crop_height : abs_crop_height;
|
||||
|
||||
if (!dst_y || !dst_u || !dst_v || !sample || src_width <= 0 ||
|
||||
crop_width <= 0 || src_height == 0 || crop_height == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// One pass rotation is available for some formats. For the rest, convert
|
||||
// to I420 (with optional vertical flipping) into a temporary I420 buffer,
|
||||
// and then rotate the I420 to the final destination buffer.
|
||||
// For in-place conversion, if destination dst_y is same as source sample,
|
||||
// also enable temporary buffer.
|
||||
if (need_buf) {
|
||||
int y_size = crop_width * abs_crop_height;
|
||||
int uv_size = ((crop_width + 1) / 2) * ((abs_crop_height + 1) / 2);
|
||||
rotate_buffer = (uint8_t *) malloc(y_size + uv_size * 2); /* NOLINT */
|
||||
if (!rotate_buffer) {
|
||||
return 1; // Out of memory runtime error.
|
||||
}
|
||||
dst_y = rotate_buffer;
|
||||
dst_u = dst_y + y_size;
|
||||
dst_v = dst_u + uv_size;
|
||||
dst_stride_y = crop_width;
|
||||
dst_stride_u = dst_stride_v = ((crop_width + 1) / 2);
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
// Single plane formats
|
||||
case FOURCC_RGBP:
|
||||
src = sample + (src_width * crop_y + crop_x) * 2;
|
||||
r = RGB565ToI420(src, src_width * 2, dst_y, dst_stride_y, dst_u,
|
||||
dst_stride_u, dst_v, dst_stride_v, crop_width,
|
||||
inv_crop_height);
|
||||
break;
|
||||
case FOURCC_ARGB:
|
||||
src = sample + (src_width * crop_y + crop_x) * 4;
|
||||
r = ARGBToI420(src, src_width * 4, dst_y, dst_stride_y, dst_u,
|
||||
dst_stride_u, dst_v, dst_stride_v, crop_width,
|
||||
inv_crop_height);
|
||||
break;
|
||||
case FOURCC_ABGR:
|
||||
src = sample + (src_width * crop_y + crop_x) * 4;
|
||||
r = ABGRToI420(src, src_width * 4, dst_y, dst_stride_y, dst_u,
|
||||
dst_stride_u, dst_v, dst_stride_v, crop_width,
|
||||
inv_crop_height);
|
||||
break;
|
||||
default:
|
||||
r = -1; // unknown fourcc - return failure code.
|
||||
}
|
||||
|
||||
if (need_buf) {
|
||||
if (!r) {
|
||||
r = I420Rotate(dst_y, dst_stride_y, dst_u, dst_stride_u, dst_v,
|
||||
dst_stride_v, tmp_y, tmp_y_stride, tmp_u, tmp_u_stride,
|
||||
tmp_v, tmp_v_stride, crop_width, abs_crop_height,
|
||||
rotation);
|
||||
}
|
||||
free(rotate_buffer);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
204
pkg/encoder/yuv/libyuv/cpu_id.c
Normal file
204
pkg/encoder/yuv/libyuv/cpu_id.c
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
* Copyright 2011 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "cpu_id.h"
|
||||
|
||||
#if !defined(__pnacl__) && !defined(__CLR_VER) && \
|
||||
!defined(__native_client__) && (defined(_M_IX86) || defined(_M_X64)) && \
|
||||
defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 160040219)
|
||||
#include <immintrin.h> // For _xgetbv()
|
||||
#endif
|
||||
|
||||
// For ArmCpuCaps() but unittested on all platforms
|
||||
#include <stdio.h> // For fopen()
|
||||
#include <string.h>
|
||||
|
||||
// For functions that use the stack and have runtime checks for overflow,
|
||||
// use SAFEBUFFERS to avoid additional check.
|
||||
#define SAFEBUFFERS
|
||||
|
||||
// cpu_info_ variable for SIMD instruction sets detected.
|
||||
LIBYUV_API int cpu_info_ = 0;
|
||||
|
||||
// Low level cpuid for X86.
|
||||
#if (defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || \
|
||||
defined(__x86_64__)) && \
|
||||
!defined(__pnacl__) && !defined(__CLR_VER)
|
||||
LIBYUV_API
|
||||
void CpuId(int info_eax, int info_ecx, int *cpu_info) {
|
||||
#if defined(_MSC_VER)
|
||||
// GCC version uses inline x86 assembly.
|
||||
#else // defined(_MSC_VER)
|
||||
int info_ebx, info_edx;
|
||||
asm volatile(
|
||||
#if defined(__i386__) && defined(__PIC__)
|
||||
// Preserve ebx for fpic 32 bit.
|
||||
"mov %%ebx, %%edi \n"
|
||||
"cpuid \n"
|
||||
"xchg %%edi, %%ebx \n"
|
||||
: "=D"(info_ebx),
|
||||
#else
|
||||
"cpuid \n"
|
||||
: "=b"(info_ebx),
|
||||
#endif // defined( __i386__) && defined(__PIC__)
|
||||
"+a"(info_eax), "+c"(info_ecx), "=d"(info_edx));
|
||||
cpu_info[0] = info_eax;
|
||||
cpu_info[1] = info_ebx;
|
||||
cpu_info[2] = info_ecx;
|
||||
cpu_info[3] = info_edx;
|
||||
#endif // defined(_MSC_VER)
|
||||
}
|
||||
|
||||
#else // (defined(_M_IX86) || defined(_M_X64) ...
|
||||
LIBYUV_API
|
||||
void CpuId(int eax, int ecx, int* cpu_info) {
|
||||
(void)eax;
|
||||
(void)ecx;
|
||||
cpu_info[0] = cpu_info[1] = cpu_info[2] = cpu_info[3] = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
// For VS2010 and earlier emit can be used:
|
||||
// _asm _emit 0x0f _asm _emit 0x01 _asm _emit 0xd0 // For VS2010 and earlier.
|
||||
// __asm {
|
||||
// xor ecx, ecx // xcr 0
|
||||
// xgetbv
|
||||
// mov xcr0, eax
|
||||
// }
|
||||
// For VS2013 and earlier 32 bit, the _xgetbv(0) optimizer produces bad code.
|
||||
// https://code.google.com/p/libyuv/issues/detail?id=529
|
||||
#if defined(_M_IX86) && defined(_MSC_VER) && (_MSC_VER < 1900)
|
||||
#pragma optimize("g", off)
|
||||
#endif
|
||||
#if (defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || \
|
||||
defined(__x86_64__)) && \
|
||||
!defined(__pnacl__) && !defined(__CLR_VER) && !defined(__native_client__)
|
||||
|
||||
// X86 CPUs have xgetbv to detect OS saves high parts of ymm registers.
|
||||
static int GetXCR0() {
|
||||
int xcr0 = 0;
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
asm(".byte 0x0f, 0x01, 0xd0" : "=a"(xcr0) : "c"(0) : "%edx");
|
||||
#endif // defined(__i386__) || defined(__x86_64__)
|
||||
return xcr0;
|
||||
}
|
||||
|
||||
#else
|
||||
// xgetbv unavailable to query for OSSave support. Return 0.
|
||||
#define GetXCR0() 0
|
||||
#endif // defined(_M_IX86) || defined(_M_X64) ..
|
||||
// Return optimization to previous setting.
|
||||
#if defined(_M_IX86) && defined(_MSC_VER) && (_MSC_VER < 1900)
|
||||
#pragma optimize("g", on)
|
||||
#endif
|
||||
|
||||
// Based on libvpx arm_cpudetect.c
|
||||
// For Arm, but public to allow testing on any CPU
|
||||
LIBYUV_API SAFEBUFFERS int ArmCpuCaps(const char *cpuinfo_name) {
|
||||
char cpuinfo_line[512];
|
||||
FILE *f = fopen(cpuinfo_name, "re");
|
||||
if (!f) {
|
||||
// Assume Neon if /proc/cpuinfo is unavailable.
|
||||
// This will occur for Chrome sandbox for Pepper or Render process.
|
||||
return kCpuHasNEON;
|
||||
}
|
||||
memset(cpuinfo_line, 0, sizeof(cpuinfo_line));
|
||||
while (fgets(cpuinfo_line, sizeof(cpuinfo_line), f)) {
|
||||
if (memcmp(cpuinfo_line, "Features", 8) == 0) {
|
||||
char *p = strstr(cpuinfo_line, " neon");
|
||||
if (p && (p[5] == ' ' || p[5] == '\n')) {
|
||||
fclose(f);
|
||||
return kCpuHasNEON;
|
||||
}
|
||||
// aarch64 uses asimd for Neon.
|
||||
p = strstr(cpuinfo_line, " asimd");
|
||||
if (p) {
|
||||
fclose(f);
|
||||
return kCpuHasNEON;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SAFEBUFFERS int GetCpuFlags(void) {
|
||||
int cpu_info = 0;
|
||||
#if !defined(__pnacl__) && !defined(__CLR_VER) && \
|
||||
(defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || \
|
||||
defined(_M_IX86))
|
||||
int cpu_info0[4] = {0, 0, 0, 0};
|
||||
int cpu_info1[4] = {0, 0, 0, 0};
|
||||
int cpu_info7[4] = {0, 0, 0, 0};
|
||||
CpuId(0, 0, cpu_info0);
|
||||
CpuId(1, 0, cpu_info1);
|
||||
if (cpu_info0[0] >= 7) {
|
||||
CpuId(7, 0, cpu_info7);
|
||||
}
|
||||
cpu_info = kCpuHasX86 | ((cpu_info1[3] & 0x04000000) ? kCpuHasSSE2 : 0) |
|
||||
((cpu_info1[2] & 0x00000200) ? kCpuHasSSSE3 : 0) |
|
||||
((cpu_info1[2] & 0x00080000) ? kCpuHasSSE41 : 0) |
|
||||
((cpu_info1[2] & 0x00100000) ? kCpuHasSSE42 : 0) |
|
||||
((cpu_info7[1] & 0x00000200) ? kCpuHasERMS : 0);
|
||||
|
||||
// AVX requires OS saves YMM registers.
|
||||
if (((cpu_info1[2] & 0x1c000000) == 0x1c000000) && // AVX and OSXSave
|
||||
((GetXCR0() & 6) == 6)) { // Test OS saves YMM registers
|
||||
cpu_info |= kCpuHasAVX | ((cpu_info7[1] & 0x00000020) ? kCpuHasAVX2 : 0) |
|
||||
((cpu_info1[2] & 0x00001000) ? kCpuHasFMA3 : 0) |
|
||||
((cpu_info1[2] & 0x20000000) ? kCpuHasF16C : 0);
|
||||
|
||||
// Detect AVX512bw
|
||||
if ((GetXCR0() & 0xe0) == 0xe0) {
|
||||
cpu_info |= (cpu_info7[1] & 0x40000000) ? kCpuHasAVX512BW : 0;
|
||||
cpu_info |= (cpu_info7[1] & 0x80000000) ? kCpuHasAVX512VL : 0;
|
||||
cpu_info |= (cpu_info7[2] & 0x00000002) ? kCpuHasAVX512VBMI : 0;
|
||||
cpu_info |= (cpu_info7[2] & 0x00000040) ? kCpuHasAVX512VBMI2 : 0;
|
||||
cpu_info |= (cpu_info7[2] & 0x00000800) ? kCpuHasAVX512VNNI : 0;
|
||||
cpu_info |= (cpu_info7[2] & 0x00001000) ? kCpuHasAVX512VBITALG : 0;
|
||||
cpu_info |= (cpu_info7[2] & 0x00004000) ? kCpuHasAVX512VPOPCNTDQ : 0;
|
||||
cpu_info |= (cpu_info7[2] & 0x00000100) ? kCpuHasGFNI : 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(__arm__) || defined(__aarch64__)
|
||||
// gcc -mfpu=neon defines __ARM_NEON__
|
||||
// __ARM_NEON__ generates code that requires Neon. NaCL also requires Neon.
|
||||
// For Linux, /proc/cpuinfo can be tested but without that assume Neon.
|
||||
#if defined(__ARM_NEON__) || defined(__native_client__) || !defined(__linux__)
|
||||
cpu_info = kCpuHasNEON;
|
||||
// For aarch64(arm64), /proc/cpuinfo's feature is not complete, e.g. no neon
|
||||
// flag in it.
|
||||
// So for aarch64, neon enabling is hard coded here.
|
||||
#endif
|
||||
#if defined(__aarch64__)
|
||||
cpu_info = kCpuHasNEON;
|
||||
#else
|
||||
// Linux arm parse text file for neon detect.
|
||||
cpu_info = ArmCpuCaps("/proc/cpuinfo");
|
||||
#endif
|
||||
cpu_info |= kCpuHasARM;
|
||||
#endif // __arm__
|
||||
cpu_info |= kCpuInitialized;
|
||||
return cpu_info;
|
||||
}
|
||||
|
||||
// Note that use of this function is not thread safe.
|
||||
LIBYUV_API
|
||||
int MaskCpuFlags(int enable_flags) {
|
||||
int cpu_info = GetCpuFlags() & enable_flags;
|
||||
SetCpuFlags(cpu_info);
|
||||
return cpu_info;
|
||||
}
|
||||
|
||||
LIBYUV_API
|
||||
int InitCpuFlags(void) {
|
||||
return MaskCpuFlags(-1);
|
||||
}
|
||||
106
pkg/encoder/yuv/libyuv/cpu_id.h
Normal file
106
pkg/encoder/yuv/libyuv/cpu_id.h
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Copyright 2011 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_LIBYUV_CPU_ID_H_
|
||||
#define INCLUDE_LIBYUV_CPU_ID_H_
|
||||
|
||||
#include "basic_types.h"
|
||||
|
||||
// Internal flag to indicate cpuid requires initialization.
|
||||
static const int kCpuInitialized = 0x1;
|
||||
|
||||
// These flags are only valid on ARM processors.
|
||||
static const int kCpuHasARM = 0x2;
|
||||
static const int kCpuHasNEON = 0x4;
|
||||
// 0x8 reserved for future ARM flag.
|
||||
|
||||
// These flags are only valid on x86 processors.
|
||||
static const int kCpuHasX86 = 0x10;
|
||||
static const int kCpuHasSSE2 = 0x20;
|
||||
static const int kCpuHasSSSE3 = 0x40;
|
||||
static const int kCpuHasSSE41 = 0x80;
|
||||
static const int kCpuHasSSE42 = 0x100; // unused at this time.
|
||||
static const int kCpuHasAVX = 0x200;
|
||||
static const int kCpuHasAVX2 = 0x400;
|
||||
static const int kCpuHasERMS = 0x800;
|
||||
static const int kCpuHasFMA3 = 0x1000;
|
||||
static const int kCpuHasF16C = 0x2000;
|
||||
static const int kCpuHasGFNI = 0x4000;
|
||||
static const int kCpuHasAVX512BW = 0x8000;
|
||||
static const int kCpuHasAVX512VL = 0x10000;
|
||||
static const int kCpuHasAVX512VNNI = 0x20000;
|
||||
static const int kCpuHasAVX512VBMI = 0x40000;
|
||||
static const int kCpuHasAVX512VBMI2 = 0x80000;
|
||||
static const int kCpuHasAVX512VBITALG = 0x100000;
|
||||
static const int kCpuHasAVX512VPOPCNTDQ = 0x200000;
|
||||
|
||||
// Optional init function. TestCpuFlag does an auto-init.
|
||||
// Returns cpu_info flags.
|
||||
LIBYUV_API
|
||||
int InitCpuFlags(void);
|
||||
|
||||
// Detect CPU has SSE2 etc.
|
||||
// Test_flag parameter should be one of kCpuHas constants above.
|
||||
// Returns non-zero if instruction set is detected
|
||||
static __inline int TestCpuFlag(int test_flag) {
|
||||
LIBYUV_API extern int cpu_info_;
|
||||
#ifdef __ATOMIC_RELAXED
|
||||
int cpu_info = __atomic_load_n(&cpu_info_, __ATOMIC_RELAXED);
|
||||
#else
|
||||
int cpu_info = cpu_info_;
|
||||
#endif
|
||||
return (!cpu_info ? InitCpuFlags() : cpu_info) & test_flag;
|
||||
}
|
||||
|
||||
// Internal function for parsing /proc/cpuinfo.
|
||||
LIBYUV_API
|
||||
int ArmCpuCaps(const char *cpuinfo_name);
|
||||
|
||||
// For testing, allow CPU flags to be disabled.
|
||||
// ie MaskCpuFlags(~kCpuHasSSSE3) to disable SSSE3.
|
||||
// MaskCpuFlags(-1) to enable all cpu specific optimizations.
|
||||
// MaskCpuFlags(1) to disable all cpu specific optimizations.
|
||||
// MaskCpuFlags(0) to reset state so next call will auto init.
|
||||
// Returns cpu_info flags.
|
||||
LIBYUV_API
|
||||
int MaskCpuFlags(int enable_flags);
|
||||
|
||||
// Sets the CPU flags to |cpu_flags|, bypassing the detection code. |cpu_flags|
|
||||
// should be a valid combination of the kCpuHas constants above and include
|
||||
// kCpuInitialized. Use this method when running in a sandboxed process where
|
||||
// the detection code might fail (as it might access /proc/cpuinfo). In such
|
||||
// cases the cpu_info can be obtained from a non sandboxed process by calling
|
||||
// InitCpuFlags() and passed to the sandboxed process (via command line
|
||||
// parameters, IPC...) which can then call this method to initialize the CPU
|
||||
// flags.
|
||||
// Notes:
|
||||
// - when specifying 0 for |cpu_flags|, the auto initialization is enabled
|
||||
// again.
|
||||
// - enabling CPU features that are not supported by the CPU will result in
|
||||
// undefined behavior.
|
||||
// TODO(fbarchard): consider writing a helper function that translates from
|
||||
// other library CPU info to libyuv CPU info and add a .md doc that explains
|
||||
// CPU detection.
|
||||
static __inline void SetCpuFlags(int cpu_flags) {
|
||||
LIBYUV_API extern int cpu_info_;
|
||||
#ifdef __ATOMIC_RELAXED
|
||||
__atomic_store_n(&cpu_info_, cpu_flags, __ATOMIC_RELAXED);
|
||||
#else
|
||||
cpu_info_ = cpu_flags;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Low level cpuid for X86. Returns zeros on other CPUs.
|
||||
// eax is the info type that you want.
|
||||
// ecx is typically the cpu number, and should normally be zero.
|
||||
LIBYUV_API
|
||||
void CpuId(int info_eax, int info_ecx, int *cpu_info);
|
||||
|
||||
#endif // INCLUDE_LIBYUV_CPU_ID_H_
|
||||
142
pkg/encoder/yuv/libyuv/libyuv.go
Normal file
142
pkg/encoder/yuv/libyuv/libyuv.go
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
//go:build !darwin && !no_libyuv
|
||||
|
||||
package libyuv
|
||||
|
||||
// see: https://chromium.googlesource.com/libyuv/libyuv
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -Wall
|
||||
#cgo LDFLAGS: -lyuv
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "libyuv/version.h"
|
||||
#include "libyuv/video_common.h"
|
||||
|
||||
//
|
||||
typedef enum RotationMode {
|
||||
kRotate0 = 0, // No rotation.
|
||||
kRotate90 = 90, // Rotate 90 degrees clockwise.
|
||||
kRotate180 = 180, // Rotate 180 degrees.
|
||||
kRotate270 = 270, // Rotate 270 degrees clockwise.
|
||||
} RotationModeEnum;
|
||||
|
||||
//
|
||||
LIBYUV_API
|
||||
int ConvertToI420(const uint8_t* sample,
|
||||
size_t sample_size,
|
||||
uint8_t* dst_y,
|
||||
int dst_stride_y,
|
||||
uint8_t* dst_u,
|
||||
int dst_stride_u,
|
||||
uint8_t* dst_v,
|
||||
int dst_stride_v,
|
||||
int crop_x,
|
||||
int crop_y,
|
||||
int src_width,
|
||||
int src_height,
|
||||
int crop_width,
|
||||
int crop_height,
|
||||
enum RotationMode rotation,
|
||||
uint32_t fourcc);
|
||||
|
||||
// Supported filtering.
|
||||
typedef enum FilterMode {
|
||||
kFilterNone = 0, // Point sample; Fastest.
|
||||
kFilterLinear = 1, // Filter horizontally only.
|
||||
kFilterBilinear = 2, // Faster than box, but lower quality scaling down.
|
||||
kFilterBox = 3 // Highest quality.
|
||||
} FilterModeEnum;
|
||||
|
||||
LIBYUV_API
|
||||
int I420Scale(const uint8_t *src_y,
|
||||
int src_stride_y,
|
||||
const uint8_t *src_u,
|
||||
int src_stride_u,
|
||||
const uint8_t *src_v,
|
||||
int src_stride_v,
|
||||
int src_width,
|
||||
int src_height,
|
||||
uint8_t *dst_y,
|
||||
int dst_stride_y,
|
||||
uint8_t *dst_u,
|
||||
int dst_stride_u,
|
||||
uint8_t *dst_v,
|
||||
int dst_stride_v,
|
||||
int dst_width,
|
||||
int dst_height,
|
||||
enum FilterMode filtering);
|
||||
*/
|
||||
import "C"
|
||||
import "fmt"
|
||||
|
||||
const FourccRgbp uint32 = C.FOURCC_RGBP
|
||||
const FourccArgb uint32 = C.FOURCC_ARGB
|
||||
const FourccAbgr uint32 = C.FOURCC_ABGR
|
||||
|
||||
func Y420(src []byte, dst []byte, _, h, stride int, dw, dh int, rot uint, pix uint32, cx, cy int) {
|
||||
cw := (dw + 1) / 2
|
||||
ch := (dh + 1) / 2
|
||||
i0 := dw * dh
|
||||
i1 := i0 + cw*ch
|
||||
yStride := dw
|
||||
cStride := cw
|
||||
|
||||
C.ConvertToI420(
|
||||
(*C.uchar)(&src[0]),
|
||||
C.size_t(0),
|
||||
(*C.uchar)(&dst[0]),
|
||||
C.int(yStride),
|
||||
(*C.uchar)(&dst[i0]),
|
||||
C.int(cStride),
|
||||
(*C.uchar)(&dst[i1]),
|
||||
C.int(cStride),
|
||||
C.int(0),
|
||||
C.int(0),
|
||||
C.int(stride),
|
||||
C.int(h),
|
||||
C.int(cx),
|
||||
C.int(cy),
|
||||
C.enum_RotationMode(rot),
|
||||
C.uint32_t(pix))
|
||||
}
|
||||
|
||||
func Y420Scale(src []byte, dst []byte, w, h int, dw, dh int) {
|
||||
srcWidthUV, dstWidthUV := (w+1)>>1, (dw+1)>>1
|
||||
srcHeightUV, dstHeightUV := (h+1)>>1, (dh+1)>>1
|
||||
|
||||
srcYPlaneSize, dstYPlaneSize := w*h, dw*dh
|
||||
srcUVPlaneSize, dstUVPlaneSize := srcWidthUV*srcHeightUV, dstWidthUV*dstHeightUV
|
||||
|
||||
srcStrideY, dstStrideY := w, dw
|
||||
srcStrideU, dstStrideU := srcWidthUV, dstWidthUV
|
||||
srcStrideV, dstStrideV := srcWidthUV, dstWidthUV
|
||||
|
||||
srcY := (*C.uchar)(&src[0])
|
||||
srcU := (*C.uchar)(&src[srcYPlaneSize])
|
||||
srcV := (*C.uchar)(&src[srcYPlaneSize+srcUVPlaneSize])
|
||||
|
||||
dstY := (*C.uchar)(&dst[0])
|
||||
dstU := (*C.uchar)(&dst[dstYPlaneSize])
|
||||
dstV := (*C.uchar)(&dst[dstYPlaneSize+dstUVPlaneSize])
|
||||
|
||||
C.I420Scale(
|
||||
srcY,
|
||||
C.int(srcStrideY),
|
||||
srcU,
|
||||
C.int(srcStrideU),
|
||||
srcV,
|
||||
C.int(srcStrideV),
|
||||
C.int(w),
|
||||
C.int(h),
|
||||
dstY,
|
||||
C.int(dstStrideY),
|
||||
dstU,
|
||||
C.int(dstStrideU),
|
||||
dstV,
|
||||
C.int(dstStrideV),
|
||||
C.int(dw),
|
||||
C.int(dh),
|
||||
C.enum_FilterMode(C.kFilterNone))
|
||||
}
|
||||
|
||||
func Version() string { return fmt.Sprintf("%v", int(C.LIBYUV_VERSION)) }
|
||||
89
pkg/encoder/yuv/libyuv/libyuv2.go
Normal file
89
pkg/encoder/yuv/libyuv/libyuv2.go
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
//go:build darwin || no_libyuv
|
||||
|
||||
package libyuv
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -Wall
|
||||
|
||||
#include "basic_types.h"
|
||||
#include "version.h"
|
||||
#include "video_common.h"
|
||||
#include "rotate.h"
|
||||
#include "scale.h"
|
||||
#include "convert.h"
|
||||
|
||||
*/
|
||||
import "C"
|
||||
import "fmt"
|
||||
|
||||
const FourccRgbp uint32 = C.FOURCC_RGBP
|
||||
const FourccArgb uint32 = C.FOURCC_ARGB
|
||||
const FourccAbgr uint32 = C.FOURCC_ABGR
|
||||
|
||||
func Y420(src []byte, dst []byte, _, h, stride int, dw, dh int, rot uint, pix uint32, cx, cy int) {
|
||||
cw := (dw + 1) / 2
|
||||
ch := (dh + 1) / 2
|
||||
i0 := dw * dh
|
||||
i1 := i0 + cw*ch
|
||||
yStride := dw
|
||||
cStride := cw
|
||||
|
||||
C.ConvertToI420(
|
||||
(*C.uchar)(&src[0]),
|
||||
C.size_t(0),
|
||||
(*C.uchar)(&dst[0]),
|
||||
C.int(yStride),
|
||||
(*C.uchar)(&dst[i0]),
|
||||
C.int(cStride),
|
||||
(*C.uchar)(&dst[i1]),
|
||||
C.int(cStride),
|
||||
C.int(0),
|
||||
C.int(0),
|
||||
C.int(stride),
|
||||
C.int(h),
|
||||
C.int(cx),
|
||||
C.int(cy),
|
||||
C.enum_RotationMode(rot),
|
||||
C.uint32_t(pix))
|
||||
}
|
||||
|
||||
func Y420Scale(src []byte, dst []byte, w, h int, dw, dh int) {
|
||||
srcWidthUV, dstWidthUV := (w+1)>>1, (dw+1)>>1
|
||||
srcHeightUV, dstHeightUV := (h+1)>>1, (dh+1)>>1
|
||||
|
||||
srcYPlaneSize, dstYPlaneSize := w*h, dw*dh
|
||||
srcUVPlaneSize, dstUVPlaneSize := srcWidthUV*srcHeightUV, dstWidthUV*dstHeightUV
|
||||
|
||||
srcStrideY, dstStrideY := w, dw
|
||||
srcStrideU, dstStrideU := srcWidthUV, dstWidthUV
|
||||
srcStrideV, dstStrideV := srcWidthUV, dstWidthUV
|
||||
|
||||
srcY := (*C.uchar)(&src[0])
|
||||
srcU := (*C.uchar)(&src[srcYPlaneSize])
|
||||
srcV := (*C.uchar)(&src[srcYPlaneSize+srcUVPlaneSize])
|
||||
|
||||
dstY := (*C.uchar)(&dst[0])
|
||||
dstU := (*C.uchar)(&dst[dstYPlaneSize])
|
||||
dstV := (*C.uchar)(&dst[dstYPlaneSize+dstUVPlaneSize])
|
||||
|
||||
C.I420Scale(
|
||||
srcY,
|
||||
C.int(srcStrideY),
|
||||
srcU,
|
||||
C.int(srcStrideU),
|
||||
srcV,
|
||||
C.int(srcStrideV),
|
||||
C.int(w),
|
||||
C.int(h),
|
||||
dstY,
|
||||
C.int(dstStrideY),
|
||||
dstU,
|
||||
C.int(dstStrideU),
|
||||
dstV,
|
||||
C.int(dstStrideV),
|
||||
C.int(dw),
|
||||
C.int(dh),
|
||||
C.enum_FilterMode(C.kFilterNone))
|
||||
}
|
||||
|
||||
func Version() string { return fmt.Sprintf("%v mod", int(C.LIBYUV_VERSION)) }
|
||||
68
pkg/encoder/yuv/libyuv/planar_functions.c
Normal file
68
pkg/encoder/yuv/libyuv/planar_functions.c
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright 2011 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "planar_functions.h"
|
||||
|
||||
#include "cpu_id.h"
|
||||
#include "row.h"
|
||||
|
||||
// Copy a plane of data
|
||||
LIBYUV_API
|
||||
void CopyPlane(const uint8_t *src_y,
|
||||
int src_stride_y,
|
||||
uint8_t *dst_y,
|
||||
int dst_stride_y,
|
||||
int width,
|
||||
int height) {
|
||||
int y;
|
||||
void (*CopyRow)(const uint8_t *src, uint8_t *dst, int width) = CopyRow_C;
|
||||
if (width <= 0 || height == 0) {
|
||||
return;
|
||||
}
|
||||
// Negative height means invert the image.
|
||||
if (height < 0) {
|
||||
height = -height;
|
||||
dst_y = dst_y + (height - 1) * dst_stride_y;
|
||||
dst_stride_y = -dst_stride_y;
|
||||
}
|
||||
// Coalesce rows.
|
||||
if (src_stride_y == width && dst_stride_y == width) {
|
||||
width *= height;
|
||||
height = 1;
|
||||
src_stride_y = dst_stride_y = 0;
|
||||
}
|
||||
// Nothing to do.
|
||||
if (src_y == dst_y && src_stride_y == dst_stride_y) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(HAS_COPYROW_SSE2)
|
||||
if (TestCpuFlag(kCpuHasSSE2)) {
|
||||
CopyRow = IS_ALIGNED(width, 32) ? CopyRow_SSE2 : CopyRow_Any_SSE2;
|
||||
}
|
||||
#endif
|
||||
#if defined(HAS_COPYROW_AVX)
|
||||
if (TestCpuFlag(kCpuHasAVX)) {
|
||||
CopyRow = IS_ALIGNED(width, 64) ? CopyRow_AVX : CopyRow_Any_AVX;
|
||||
}
|
||||
#endif
|
||||
#if defined(HAS_COPYROW_ERMS)
|
||||
if (TestCpuFlag(kCpuHasERMS)) {
|
||||
CopyRow = CopyRow_ERMS;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Copy plane
|
||||
for (y = 0; y < height; ++y) {
|
||||
CopyRow(src_y, dst_y, width);
|
||||
src_y += src_stride_y;
|
||||
dst_y += dst_stride_y;
|
||||
}
|
||||
}
|
||||
46
pkg/encoder/yuv/libyuv/planar_functions.h
Normal file
46
pkg/encoder/yuv/libyuv/planar_functions.h
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright 2011 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_LIBYUV_PLANAR_FUNCTIONS_H_
|
||||
#define INCLUDE_LIBYUV_PLANAR_FUNCTIONS_H_
|
||||
|
||||
#include "basic_types.h"
|
||||
|
||||
// TODO(fbarchard): Move cpu macros to row.h
|
||||
#if defined(__pnacl__) || defined(__CLR_VER) || \
|
||||
(defined(__native_client__) && defined(__x86_64__)) || \
|
||||
(defined(__i386__) && !defined(__SSE__) && !defined(__clang__))
|
||||
#define LIBYUV_DISABLE_X86
|
||||
#endif
|
||||
// MemorySanitizer does not support assembly code yet. http://crbug.com/344505
|
||||
#if defined(__has_feature)
|
||||
#if __has_feature(memory_sanitizer) && !defined(LIBYUV_DISABLE_NEON)
|
||||
#define LIBYUV_DISABLE_NEON
|
||||
#endif
|
||||
#if __has_feature(memory_sanitizer) && !defined(LIBYUV_DISABLE_X86)
|
||||
#define LIBYUV_DISABLE_X86
|
||||
#endif
|
||||
#endif
|
||||
// The following are available on all x86 platforms:
|
||||
#if !defined(LIBYUV_DISABLE_X86) && \
|
||||
(defined(_M_IX86) || defined(__x86_64__) || defined(__i386__))
|
||||
#define HAS_ARGBAFFINEROW_SSE2
|
||||
#endif
|
||||
|
||||
// Copy a plane of data.
|
||||
LIBYUV_API
|
||||
void CopyPlane(const uint8_t *src_y,
|
||||
int src_stride_y,
|
||||
uint8_t *dst_y,
|
||||
int dst_stride_y,
|
||||
int width,
|
||||
int height);
|
||||
|
||||
#endif // INCLUDE_LIBYUV_PLANAR_FUNCTIONS_H_
|
||||
217
pkg/encoder/yuv/libyuv/rotate.c
Normal file
217
pkg/encoder/yuv/libyuv/rotate.c
Normal file
|
|
@ -0,0 +1,217 @@
|
|||
/*
|
||||
* Copyright 2011 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "rotate.h"
|
||||
|
||||
#include "convert.h"
|
||||
#include "cpu_id.h"
|
||||
#include "rotate_row.h"
|
||||
#include "row.h"
|
||||
|
||||
LIBYUV_API
|
||||
void TransposePlane(const uint8_t *src,
|
||||
int src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_stride,
|
||||
int width,
|
||||
int height) {
|
||||
int i = height;
|
||||
|
||||
void (*TransposeWx8)(const uint8_t *src, int src_stride, uint8_t *dst,
|
||||
int dst_stride, int width) = TransposeWx8_C;
|
||||
|
||||
#if defined(HAS_TRANSPOSEWX8_SSSE3)
|
||||
if (TestCpuFlag(kCpuHasSSSE3)) {
|
||||
TransposeWx8 = TransposeWx8_Any_SSSE3;
|
||||
if (IS_ALIGNED(width, 8)) {
|
||||
TransposeWx8 = TransposeWx8_SSSE3;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(HAS_TRANSPOSEWX8_FAST_SSSE3)
|
||||
if (TestCpuFlag(kCpuHasSSSE3)) {
|
||||
TransposeWx8 = TransposeWx8_Fast_Any_SSSE3;
|
||||
if (IS_ALIGNED(width, 16)) {
|
||||
TransposeWx8 = TransposeWx8_Fast_SSSE3;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Work across the source in 8x8 tiles
|
||||
while (i >= 8) {
|
||||
TransposeWx8(src, src_stride, dst, dst_stride, width);
|
||||
src += 8 * src_stride; // Go down 8 rows.
|
||||
dst += 8; // Move over 8 columns.
|
||||
i -= 8;
|
||||
}
|
||||
|
||||
if (i > 0) {
|
||||
TransposeWxH_C(src, src_stride, dst, dst_stride, width, i);
|
||||
}
|
||||
}
|
||||
|
||||
LIBYUV_API
|
||||
void RotatePlane90(const uint8_t *src,
|
||||
int src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_stride,
|
||||
int width,
|
||||
int height) {
|
||||
// Rotate by 90 is a transpose with the source read
|
||||
// from bottom to top. So set the source pointer to the end
|
||||
// of the buffer and flip the sign of the source stride.
|
||||
src += src_stride * (height - 1);
|
||||
src_stride = -src_stride;
|
||||
TransposePlane(src, src_stride, dst, dst_stride, width, height);
|
||||
}
|
||||
|
||||
LIBYUV_API
|
||||
void RotatePlane270(const uint8_t *src,
|
||||
int src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_stride,
|
||||
int width,
|
||||
int height) {
|
||||
// Rotate by 270 is a transpose with the destination written
|
||||
// from bottom to top. So set the destination pointer to the end
|
||||
// of the buffer and flip the sign of the destination stride.
|
||||
dst += dst_stride * (width - 1);
|
||||
dst_stride = -dst_stride;
|
||||
TransposePlane(src, src_stride, dst, dst_stride, width, height);
|
||||
}
|
||||
|
||||
LIBYUV_API
|
||||
void RotatePlane180(const uint8_t *src,
|
||||
int src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_stride,
|
||||
int width,
|
||||
int height) {
|
||||
// Swap top and bottom row and mirror the content. Uses a temporary row.
|
||||
align_buffer_64(row, width);
|
||||
const uint8_t *src_bot = src + src_stride * (height - 1);
|
||||
uint8_t *dst_bot = dst + dst_stride * (height - 1);
|
||||
int half_height = (height + 1) >> 1;
|
||||
int y;
|
||||
void (*MirrorRow)(const uint8_t *src, uint8_t *dst, int width) = MirrorRow_C;
|
||||
void (*CopyRow)(const uint8_t *src, uint8_t *dst, int width) = CopyRow_C;
|
||||
#if defined(HAS_MIRRORROW_SSSE3)
|
||||
if (TestCpuFlag(kCpuHasSSSE3)) {
|
||||
MirrorRow = MirrorRow_Any_SSSE3;
|
||||
if (IS_ALIGNED(width, 16)) {
|
||||
MirrorRow = MirrorRow_SSSE3;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(HAS_MIRRORROW_AVX2)
|
||||
if (TestCpuFlag(kCpuHasAVX2)) {
|
||||
MirrorRow = MirrorRow_Any_AVX2;
|
||||
if (IS_ALIGNED(width, 32)) {
|
||||
MirrorRow = MirrorRow_AVX2;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(HAS_COPYROW_SSE2)
|
||||
if (TestCpuFlag(kCpuHasSSE2)) {
|
||||
CopyRow = IS_ALIGNED(width, 32) ? CopyRow_SSE2 : CopyRow_Any_SSE2;
|
||||
}
|
||||
#endif
|
||||
#if defined(HAS_COPYROW_AVX)
|
||||
if (TestCpuFlag(kCpuHasAVX)) {
|
||||
CopyRow = IS_ALIGNED(width, 64) ? CopyRow_AVX : CopyRow_Any_AVX;
|
||||
}
|
||||
#endif
|
||||
#if defined(HAS_COPYROW_ERMS)
|
||||
if (TestCpuFlag(kCpuHasERMS)) {
|
||||
CopyRow = CopyRow_ERMS;
|
||||
}
|
||||
#endif
|
||||
#if defined(HAS_COPYROW_NEON)
|
||||
#endif
|
||||
// Odd height will harmlessly mirror the middle row twice.
|
||||
for (y = 0; y < half_height; ++y) {
|
||||
CopyRow(src, row, width); // Copy top row into buffer
|
||||
MirrorRow(src_bot, dst, width); // Mirror bottom row into top row
|
||||
MirrorRow(row, dst_bot, width); // Mirror buffer into bottom row
|
||||
src += src_stride;
|
||||
dst += dst_stride;
|
||||
src_bot -= src_stride;
|
||||
dst_bot -= dst_stride;
|
||||
}
|
||||
free_aligned_buffer_64(row);
|
||||
}
|
||||
|
||||
LIBYUV_API
|
||||
int I420Rotate(const uint8_t *src_y,
|
||||
int src_stride_y,
|
||||
const uint8_t *src_u,
|
||||
int src_stride_u,
|
||||
const uint8_t *src_v,
|
||||
int src_stride_v,
|
||||
uint8_t *dst_y,
|
||||
int dst_stride_y,
|
||||
uint8_t *dst_u,
|
||||
int dst_stride_u,
|
||||
uint8_t *dst_v,
|
||||
int dst_stride_v,
|
||||
int width,
|
||||
int height,
|
||||
enum RotationMode mode) {
|
||||
int halfwidth = (width + 1) >> 1;
|
||||
int halfheight = (height + 1) >> 1;
|
||||
if ((!src_y && dst_y) || !src_u || !src_v || width <= 0 || height == 0 ||
|
||||
!dst_y || !dst_u || !dst_v) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Negative height means invert the image.
|
||||
if (height < 0) {
|
||||
height = -height;
|
||||
halfheight = (height + 1) >> 1;
|
||||
src_y = src_y + (height - 1) * src_stride_y;
|
||||
src_u = src_u + (halfheight - 1) * src_stride_u;
|
||||
src_v = src_v + (halfheight - 1) * src_stride_v;
|
||||
src_stride_y = -src_stride_y;
|
||||
src_stride_u = -src_stride_u;
|
||||
src_stride_v = -src_stride_v;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case kRotate0:
|
||||
// copy frame
|
||||
return I420Copy(src_y, src_stride_y, src_u, src_stride_u, src_v,
|
||||
src_stride_v, dst_y, dst_stride_y, dst_u, dst_stride_u,
|
||||
dst_v, dst_stride_v, width, height);
|
||||
case kRotate90:
|
||||
RotatePlane90(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
|
||||
RotatePlane90(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth,
|
||||
halfheight);
|
||||
RotatePlane90(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth,
|
||||
halfheight);
|
||||
return 0;
|
||||
case kRotate270:
|
||||
RotatePlane270(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
|
||||
RotatePlane270(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth,
|
||||
halfheight);
|
||||
RotatePlane270(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth,
|
||||
halfheight);
|
||||
return 0;
|
||||
case kRotate180:
|
||||
RotatePlane180(src_y, src_stride_y, dst_y, dst_stride_y, width, height);
|
||||
RotatePlane180(src_u, src_stride_u, dst_u, dst_stride_u, halfwidth,
|
||||
halfheight);
|
||||
RotatePlane180(src_v, src_stride_v, dst_v, dst_stride_v, halfwidth,
|
||||
halfheight);
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
79
pkg/encoder/yuv/libyuv/rotate.h
Normal file
79
pkg/encoder/yuv/libyuv/rotate.h
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* Copyright 2011 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_LIBYUV_ROTATE_H_
|
||||
#define INCLUDE_LIBYUV_ROTATE_H_
|
||||
|
||||
#include "basic_types.h"
|
||||
|
||||
// Supported rotation.
|
||||
typedef enum RotationMode {
|
||||
kRotate0 = 0, // No rotation.
|
||||
kRotate90 = 90, // Rotate 90 degrees clockwise.
|
||||
kRotate180 = 180, // Rotate 180 degrees.
|
||||
kRotate270 = 270, // Rotate 270 degrees clockwise.
|
||||
} RotationModeEnum;
|
||||
|
||||
// Rotate I420 frame.
|
||||
LIBYUV_API
|
||||
int I420Rotate(const uint8_t *src_y,
|
||||
int src_stride_y,
|
||||
const uint8_t *src_u,
|
||||
int src_stride_u,
|
||||
const uint8_t *src_v,
|
||||
int src_stride_v,
|
||||
uint8_t *dst_y,
|
||||
int dst_stride_y,
|
||||
uint8_t *dst_u,
|
||||
int dst_stride_u,
|
||||
uint8_t *dst_v,
|
||||
int dst_stride_v,
|
||||
int width,
|
||||
int height,
|
||||
enum RotationMode mode);
|
||||
|
||||
// Rotate planes by 90, 180, 270. Deprecated.
|
||||
LIBYUV_API
|
||||
void RotatePlane90(const uint8_t *src,
|
||||
int src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_stride,
|
||||
int width,
|
||||
int height);
|
||||
|
||||
LIBYUV_API
|
||||
void RotatePlane180(const uint8_t *src,
|
||||
int src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_stride,
|
||||
int width,
|
||||
int height);
|
||||
|
||||
LIBYUV_API
|
||||
void RotatePlane270(const uint8_t *src,
|
||||
int src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_stride,
|
||||
int width,
|
||||
int height);
|
||||
|
||||
// The 90 and 270 functions are based on transposes.
|
||||
// Doing a transpose with reversing the read/write
|
||||
// order will result in a rotation by +- 90 degrees.
|
||||
// Deprecated.
|
||||
LIBYUV_API
|
||||
void TransposePlane(const uint8_t *src,
|
||||
int src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_stride,
|
||||
int width,
|
||||
int height);
|
||||
|
||||
#endif // INCLUDE_LIBYUV_ROTATE_H_
|
||||
54
pkg/encoder/yuv/libyuv/rotate_any.c
Normal file
54
pkg/encoder/yuv/libyuv/rotate_any.c
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright 2015 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "rotate_row.h"
|
||||
|
||||
#define TANY(NAMEANY, TPOS_SIMD, MASK) \
|
||||
void NAMEANY(const uint8_t* src, int src_stride, uint8_t* dst, \
|
||||
int dst_stride, int width) { \
|
||||
int r = width & MASK; \
|
||||
int n = width - r; \
|
||||
if (n > 0) { \
|
||||
TPOS_SIMD(src, src_stride, dst, dst_stride, n); \
|
||||
} \
|
||||
TransposeWx8_C(src + n, src_stride, dst + n * dst_stride, dst_stride, r); \
|
||||
}
|
||||
|
||||
#ifdef HAS_TRANSPOSEWX8_SSSE3
|
||||
|
||||
TANY(TransposeWx8_Any_SSSE3, TransposeWx8_SSSE3, 7)
|
||||
|
||||
#endif
|
||||
#ifdef HAS_TRANSPOSEWX8_FAST_SSSE3
|
||||
|
||||
TANY(TransposeWx8_Fast_Any_SSSE3, TransposeWx8_Fast_SSSE3, 15)
|
||||
|
||||
#endif
|
||||
#undef TANY
|
||||
|
||||
#define TUVANY(NAMEANY, TPOS_SIMD, MASK) \
|
||||
void NAMEANY(const uint8_t* src, int src_stride, uint8_t* dst_a, \
|
||||
int dst_stride_a, uint8_t* dst_b, int dst_stride_b, \
|
||||
int width) { \
|
||||
int r = width & MASK; \
|
||||
int n = width - r; \
|
||||
if (n > 0) { \
|
||||
TPOS_SIMD(src, src_stride, dst_a, dst_stride_a, dst_b, dst_stride_b, n); \
|
||||
} \
|
||||
TransposeUVWx8_C(src + n * 2, src_stride, dst_a + n * dst_stride_a, \
|
||||
dst_stride_a, dst_b + n * dst_stride_b, dst_stride_b, r); \
|
||||
}
|
||||
|
||||
#ifdef HAS_TRANSPOSEUVWX8_SSE2
|
||||
|
||||
TUVANY(TransposeUVWx8_Any_SSE2, TransposeUVWx8_SSE2, 7)
|
||||
|
||||
#endif
|
||||
#undef TUVANY
|
||||
77
pkg/encoder/yuv/libyuv/rotate_common.c
Normal file
77
pkg/encoder/yuv/libyuv/rotate_common.c
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright 2011 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "rotate_row.h"
|
||||
|
||||
void TransposeWx8_C(const uint8_t *src,
|
||||
int src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_stride,
|
||||
int width) {
|
||||
int i;
|
||||
for (i = 0; i < width; ++i) {
|
||||
dst[0] = src[0 * src_stride];
|
||||
dst[1] = src[1 * src_stride];
|
||||
dst[2] = src[2 * src_stride];
|
||||
dst[3] = src[3 * src_stride];
|
||||
dst[4] = src[4 * src_stride];
|
||||
dst[5] = src[5 * src_stride];
|
||||
dst[6] = src[6 * src_stride];
|
||||
dst[7] = src[7 * src_stride];
|
||||
++src;
|
||||
dst += dst_stride;
|
||||
}
|
||||
}
|
||||
|
||||
void TransposeUVWx8_C(const uint8_t *src,
|
||||
int src_stride,
|
||||
uint8_t *dst_a,
|
||||
int dst_stride_a,
|
||||
uint8_t *dst_b,
|
||||
int dst_stride_b,
|
||||
int width) {
|
||||
int i;
|
||||
for (i = 0; i < width; ++i) {
|
||||
dst_a[0] = src[0 * src_stride + 0];
|
||||
dst_b[0] = src[0 * src_stride + 1];
|
||||
dst_a[1] = src[1 * src_stride + 0];
|
||||
dst_b[1] = src[1 * src_stride + 1];
|
||||
dst_a[2] = src[2 * src_stride + 0];
|
||||
dst_b[2] = src[2 * src_stride + 1];
|
||||
dst_a[3] = src[3 * src_stride + 0];
|
||||
dst_b[3] = src[3 * src_stride + 1];
|
||||
dst_a[4] = src[4 * src_stride + 0];
|
||||
dst_b[4] = src[4 * src_stride + 1];
|
||||
dst_a[5] = src[5 * src_stride + 0];
|
||||
dst_b[5] = src[5 * src_stride + 1];
|
||||
dst_a[6] = src[6 * src_stride + 0];
|
||||
dst_b[6] = src[6 * src_stride + 1];
|
||||
dst_a[7] = src[7 * src_stride + 0];
|
||||
dst_b[7] = src[7 * src_stride + 1];
|
||||
src += 2;
|
||||
dst_a += dst_stride_a;
|
||||
dst_b += dst_stride_b;
|
||||
}
|
||||
}
|
||||
|
||||
void TransposeWxH_C(const uint8_t *src,
|
||||
int src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_stride,
|
||||
int width,
|
||||
int height) {
|
||||
int i;
|
||||
for (i = 0; i < width; ++i) {
|
||||
int j;
|
||||
for (j = 0; j < height; ++j) {
|
||||
dst[i * dst_stride + j] = src[j * src_stride + i];
|
||||
}
|
||||
}
|
||||
}
|
||||
370
pkg/encoder/yuv/libyuv/rotate_gcc.c
Normal file
370
pkg/encoder/yuv/libyuv/rotate_gcc.c
Normal file
|
|
@ -0,0 +1,370 @@
|
|||
/*
|
||||
* Copyright 2015 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "rotate_row.h"
|
||||
#include "row.h"
|
||||
|
||||
// This module is for GCC x86 and x64.
|
||||
#if !defined(LIBYUV_DISABLE_X86) && (defined(__x86_64__) || defined(__i386__))
|
||||
|
||||
// Transpose 8x8. 32 or 64 bit, but not NaCL for 64 bit.
|
||||
#if defined(HAS_TRANSPOSEWX8_SSSE3)
|
||||
|
||||
void TransposeWx8_SSSE3(const uint8_t *src,
|
||||
int src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_stride,
|
||||
int width) {
|
||||
asm volatile(
|
||||
// Read in the data from the source pointer.
|
||||
// First round of bit swap.
|
||||
LABELALIGN
|
||||
"1: \n"
|
||||
"movq (%0),%%xmm0 \n"
|
||||
"movq (%0,%3),%%xmm1 \n"
|
||||
"lea (%0,%3,2),%0 \n"
|
||||
"punpcklbw %%xmm1,%%xmm0 \n"
|
||||
"movq (%0),%%xmm2 \n"
|
||||
"movdqa %%xmm0,%%xmm1 \n"
|
||||
"palignr $0x8,%%xmm1,%%xmm1 \n"
|
||||
"movq (%0,%3),%%xmm3 \n"
|
||||
"lea (%0,%3,2),%0 \n"
|
||||
"punpcklbw %%xmm3,%%xmm2 \n"
|
||||
"movdqa %%xmm2,%%xmm3 \n"
|
||||
"movq (%0),%%xmm4 \n"
|
||||
"palignr $0x8,%%xmm3,%%xmm3 \n"
|
||||
"movq (%0,%3),%%xmm5 \n"
|
||||
"lea (%0,%3,2),%0 \n"
|
||||
"punpcklbw %%xmm5,%%xmm4 \n"
|
||||
"movdqa %%xmm4,%%xmm5 \n"
|
||||
"movq (%0),%%xmm6 \n"
|
||||
"palignr $0x8,%%xmm5,%%xmm5 \n"
|
||||
"movq (%0,%3),%%xmm7 \n"
|
||||
"lea (%0,%3,2),%0 \n"
|
||||
"punpcklbw %%xmm7,%%xmm6 \n"
|
||||
"neg %3 \n"
|
||||
"movdqa %%xmm6,%%xmm7 \n"
|
||||
"lea 0x8(%0,%3,8),%0 \n"
|
||||
"palignr $0x8,%%xmm7,%%xmm7 \n"
|
||||
"neg %3 \n"
|
||||
// Second round of bit swap.
|
||||
"punpcklwd %%xmm2,%%xmm0 \n"
|
||||
"punpcklwd %%xmm3,%%xmm1 \n"
|
||||
"movdqa %%xmm0,%%xmm2 \n"
|
||||
"movdqa %%xmm1,%%xmm3 \n"
|
||||
"palignr $0x8,%%xmm2,%%xmm2 \n"
|
||||
"palignr $0x8,%%xmm3,%%xmm3 \n"
|
||||
"punpcklwd %%xmm6,%%xmm4 \n"
|
||||
"punpcklwd %%xmm7,%%xmm5 \n"
|
||||
"movdqa %%xmm4,%%xmm6 \n"
|
||||
"movdqa %%xmm5,%%xmm7 \n"
|
||||
"palignr $0x8,%%xmm6,%%xmm6 \n"
|
||||
"palignr $0x8,%%xmm7,%%xmm7 \n"
|
||||
// Third round of bit swap.
|
||||
// Write to the destination pointer.
|
||||
"punpckldq %%xmm4,%%xmm0 \n"
|
||||
"movq %%xmm0,(%1) \n"
|
||||
"movdqa %%xmm0,%%xmm4 \n"
|
||||
"palignr $0x8,%%xmm4,%%xmm4 \n"
|
||||
"movq %%xmm4,(%1,%4) \n"
|
||||
"lea (%1,%4,2),%1 \n"
|
||||
"punpckldq %%xmm6,%%xmm2 \n"
|
||||
"movdqa %%xmm2,%%xmm6 \n"
|
||||
"movq %%xmm2,(%1) \n"
|
||||
"palignr $0x8,%%xmm6,%%xmm6 \n"
|
||||
"punpckldq %%xmm5,%%xmm1 \n"
|
||||
"movq %%xmm6,(%1,%4) \n"
|
||||
"lea (%1,%4,2),%1 \n"
|
||||
"movdqa %%xmm1,%%xmm5 \n"
|
||||
"movq %%xmm1,(%1) \n"
|
||||
"palignr $0x8,%%xmm5,%%xmm5 \n"
|
||||
"movq %%xmm5,(%1,%4) \n"
|
||||
"lea (%1,%4,2),%1 \n"
|
||||
"punpckldq %%xmm7,%%xmm3 \n"
|
||||
"movq %%xmm3,(%1) \n"
|
||||
"movdqa %%xmm3,%%xmm7 \n"
|
||||
"palignr $0x8,%%xmm7,%%xmm7 \n"
|
||||
"sub $0x8,%2 \n"
|
||||
"movq %%xmm7,(%1,%4) \n"
|
||||
"lea (%1,%4,2),%1 \n"
|
||||
"jg 1b \n"
|
||||
: "+r"(src), // %0
|
||||
"+r"(dst), // %1
|
||||
"+r"(width) // %2
|
||||
: "r"((intptr_t) (src_stride)), // %3
|
||||
"r"((intptr_t) (dst_stride)) // %4
|
||||
: "memory", "cc", "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6",
|
||||
"xmm7");
|
||||
}
|
||||
|
||||
#endif // defined(HAS_TRANSPOSEWX8_SSSE3)
|
||||
|
||||
// Transpose 16x8. 64 bit
|
||||
#if defined(HAS_TRANSPOSEWX8_FAST_SSSE3)
|
||||
|
||||
void TransposeWx8_Fast_SSSE3(const uint8_t *src,
|
||||
int src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_stride,
|
||||
int width) {
|
||||
asm volatile(
|
||||
// Read in the data from the source pointer.
|
||||
// First round of bit swap.
|
||||
LABELALIGN
|
||||
"1: \n"
|
||||
"movdqu (%0),%%xmm0 \n"
|
||||
"movdqu (%0,%3),%%xmm1 \n"
|
||||
"lea (%0,%3,2),%0 \n"
|
||||
"movdqa %%xmm0,%%xmm8 \n"
|
||||
"punpcklbw %%xmm1,%%xmm0 \n"
|
||||
"punpckhbw %%xmm1,%%xmm8 \n"
|
||||
"movdqu (%0),%%xmm2 \n"
|
||||
"movdqa %%xmm0,%%xmm1 \n"
|
||||
"movdqa %%xmm8,%%xmm9 \n"
|
||||
"palignr $0x8,%%xmm1,%%xmm1 \n"
|
||||
"palignr $0x8,%%xmm9,%%xmm9 \n"
|
||||
"movdqu (%0,%3),%%xmm3 \n"
|
||||
"lea (%0,%3,2),%0 \n"
|
||||
"movdqa %%xmm2,%%xmm10 \n"
|
||||
"punpcklbw %%xmm3,%%xmm2 \n"
|
||||
"punpckhbw %%xmm3,%%xmm10 \n"
|
||||
"movdqa %%xmm2,%%xmm3 \n"
|
||||
"movdqa %%xmm10,%%xmm11 \n"
|
||||
"movdqu (%0),%%xmm4 \n"
|
||||
"palignr $0x8,%%xmm3,%%xmm3 \n"
|
||||
"palignr $0x8,%%xmm11,%%xmm11 \n"
|
||||
"movdqu (%0,%3),%%xmm5 \n"
|
||||
"lea (%0,%3,2),%0 \n"
|
||||
"movdqa %%xmm4,%%xmm12 \n"
|
||||
"punpcklbw %%xmm5,%%xmm4 \n"
|
||||
"punpckhbw %%xmm5,%%xmm12 \n"
|
||||
"movdqa %%xmm4,%%xmm5 \n"
|
||||
"movdqa %%xmm12,%%xmm13 \n"
|
||||
"movdqu (%0),%%xmm6 \n"
|
||||
"palignr $0x8,%%xmm5,%%xmm5 \n"
|
||||
"palignr $0x8,%%xmm13,%%xmm13 \n"
|
||||
"movdqu (%0,%3),%%xmm7 \n"
|
||||
"lea (%0,%3,2),%0 \n"
|
||||
"movdqa %%xmm6,%%xmm14 \n"
|
||||
"punpcklbw %%xmm7,%%xmm6 \n"
|
||||
"punpckhbw %%xmm7,%%xmm14 \n"
|
||||
"neg %3 \n"
|
||||
"movdqa %%xmm6,%%xmm7 \n"
|
||||
"movdqa %%xmm14,%%xmm15 \n"
|
||||
"lea 0x10(%0,%3,8),%0 \n"
|
||||
"palignr $0x8,%%xmm7,%%xmm7 \n"
|
||||
"palignr $0x8,%%xmm15,%%xmm15 \n"
|
||||
"neg %3 \n"
|
||||
// Second round of bit swap.
|
||||
"punpcklwd %%xmm2,%%xmm0 \n"
|
||||
"punpcklwd %%xmm3,%%xmm1 \n"
|
||||
"movdqa %%xmm0,%%xmm2 \n"
|
||||
"movdqa %%xmm1,%%xmm3 \n"
|
||||
"palignr $0x8,%%xmm2,%%xmm2 \n"
|
||||
"palignr $0x8,%%xmm3,%%xmm3 \n"
|
||||
"punpcklwd %%xmm6,%%xmm4 \n"
|
||||
"punpcklwd %%xmm7,%%xmm5 \n"
|
||||
"movdqa %%xmm4,%%xmm6 \n"
|
||||
"movdqa %%xmm5,%%xmm7 \n"
|
||||
"palignr $0x8,%%xmm6,%%xmm6 \n"
|
||||
"palignr $0x8,%%xmm7,%%xmm7 \n"
|
||||
"punpcklwd %%xmm10,%%xmm8 \n"
|
||||
"punpcklwd %%xmm11,%%xmm9 \n"
|
||||
"movdqa %%xmm8,%%xmm10 \n"
|
||||
"movdqa %%xmm9,%%xmm11 \n"
|
||||
"palignr $0x8,%%xmm10,%%xmm10 \n"
|
||||
"palignr $0x8,%%xmm11,%%xmm11 \n"
|
||||
"punpcklwd %%xmm14,%%xmm12 \n"
|
||||
"punpcklwd %%xmm15,%%xmm13 \n"
|
||||
"movdqa %%xmm12,%%xmm14 \n"
|
||||
"movdqa %%xmm13,%%xmm15 \n"
|
||||
"palignr $0x8,%%xmm14,%%xmm14 \n"
|
||||
"palignr $0x8,%%xmm15,%%xmm15 \n"
|
||||
// Third round of bit swap.
|
||||
// Write to the destination pointer.
|
||||
"punpckldq %%xmm4,%%xmm0 \n"
|
||||
"movq %%xmm0,(%1) \n"
|
||||
"movdqa %%xmm0,%%xmm4 \n"
|
||||
"palignr $0x8,%%xmm4,%%xmm4 \n"
|
||||
"movq %%xmm4,(%1,%4) \n"
|
||||
"lea (%1,%4,2),%1 \n"
|
||||
"punpckldq %%xmm6,%%xmm2 \n"
|
||||
"movdqa %%xmm2,%%xmm6 \n"
|
||||
"movq %%xmm2,(%1) \n"
|
||||
"palignr $0x8,%%xmm6,%%xmm6 \n"
|
||||
"punpckldq %%xmm5,%%xmm1 \n"
|
||||
"movq %%xmm6,(%1,%4) \n"
|
||||
"lea (%1,%4,2),%1 \n"
|
||||
"movdqa %%xmm1,%%xmm5 \n"
|
||||
"movq %%xmm1,(%1) \n"
|
||||
"palignr $0x8,%%xmm5,%%xmm5 \n"
|
||||
"movq %%xmm5,(%1,%4) \n"
|
||||
"lea (%1,%4,2),%1 \n"
|
||||
"punpckldq %%xmm7,%%xmm3 \n"
|
||||
"movq %%xmm3,(%1) \n"
|
||||
"movdqa %%xmm3,%%xmm7 \n"
|
||||
"palignr $0x8,%%xmm7,%%xmm7 \n"
|
||||
"movq %%xmm7,(%1,%4) \n"
|
||||
"lea (%1,%4,2),%1 \n"
|
||||
"punpckldq %%xmm12,%%xmm8 \n"
|
||||
"movq %%xmm8,(%1) \n"
|
||||
"movdqa %%xmm8,%%xmm12 \n"
|
||||
"palignr $0x8,%%xmm12,%%xmm12 \n"
|
||||
"movq %%xmm12,(%1,%4) \n"
|
||||
"lea (%1,%4,2),%1 \n"
|
||||
"punpckldq %%xmm14,%%xmm10 \n"
|
||||
"movdqa %%xmm10,%%xmm14 \n"
|
||||
"movq %%xmm10,(%1) \n"
|
||||
"palignr $0x8,%%xmm14,%%xmm14 \n"
|
||||
"punpckldq %%xmm13,%%xmm9 \n"
|
||||
"movq %%xmm14,(%1,%4) \n"
|
||||
"lea (%1,%4,2),%1 \n"
|
||||
"movdqa %%xmm9,%%xmm13 \n"
|
||||
"movq %%xmm9,(%1) \n"
|
||||
"palignr $0x8,%%xmm13,%%xmm13 \n"
|
||||
"movq %%xmm13,(%1,%4) \n"
|
||||
"lea (%1,%4,2),%1 \n"
|
||||
"punpckldq %%xmm15,%%xmm11 \n"
|
||||
"movq %%xmm11,(%1) \n"
|
||||
"movdqa %%xmm11,%%xmm15 \n"
|
||||
"palignr $0x8,%%xmm15,%%xmm15 \n"
|
||||
"sub $0x10,%2 \n"
|
||||
"movq %%xmm15,(%1,%4) \n"
|
||||
"lea (%1,%4,2),%1 \n"
|
||||
"jg 1b \n"
|
||||
: "+r"(src), // %0
|
||||
"+r"(dst), // %1
|
||||
"+r"(width) // %2
|
||||
: "r"((intptr_t) (src_stride)), // %3
|
||||
"r"((intptr_t) (dst_stride)) // %4
|
||||
: "memory", "cc", "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6",
|
||||
"xmm7", "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14",
|
||||
"xmm15");
|
||||
}
|
||||
|
||||
#endif // defined(HAS_TRANSPOSEWX8_FAST_SSSE3)
|
||||
|
||||
// Transpose UV 8x8. 64 bit.
|
||||
#if defined(HAS_TRANSPOSEUVWX8_SSE2)
|
||||
|
||||
void TransposeUVWx8_SSE2(const uint8_t *src,
|
||||
int src_stride,
|
||||
uint8_t *dst_a,
|
||||
int dst_stride_a,
|
||||
uint8_t *dst_b,
|
||||
int dst_stride_b,
|
||||
int width) {
|
||||
asm volatile(
|
||||
// Read in the data from the source pointer.
|
||||
// First round of bit swap.
|
||||
LABELALIGN
|
||||
"1: \n"
|
||||
"movdqu (%0),%%xmm0 \n"
|
||||
"movdqu (%0,%4),%%xmm1 \n"
|
||||
"lea (%0,%4,2),%0 \n"
|
||||
"movdqa %%xmm0,%%xmm8 \n"
|
||||
"punpcklbw %%xmm1,%%xmm0 \n"
|
||||
"punpckhbw %%xmm1,%%xmm8 \n"
|
||||
"movdqa %%xmm8,%%xmm1 \n"
|
||||
"movdqu (%0),%%xmm2 \n"
|
||||
"movdqu (%0,%4),%%xmm3 \n"
|
||||
"lea (%0,%4,2),%0 \n"
|
||||
"movdqa %%xmm2,%%xmm8 \n"
|
||||
"punpcklbw %%xmm3,%%xmm2 \n"
|
||||
"punpckhbw %%xmm3,%%xmm8 \n"
|
||||
"movdqa %%xmm8,%%xmm3 \n"
|
||||
"movdqu (%0),%%xmm4 \n"
|
||||
"movdqu (%0,%4),%%xmm5 \n"
|
||||
"lea (%0,%4,2),%0 \n"
|
||||
"movdqa %%xmm4,%%xmm8 \n"
|
||||
"punpcklbw %%xmm5,%%xmm4 \n"
|
||||
"punpckhbw %%xmm5,%%xmm8 \n"
|
||||
"movdqa %%xmm8,%%xmm5 \n"
|
||||
"movdqu (%0),%%xmm6 \n"
|
||||
"movdqu (%0,%4),%%xmm7 \n"
|
||||
"lea (%0,%4,2),%0 \n"
|
||||
"movdqa %%xmm6,%%xmm8 \n"
|
||||
"punpcklbw %%xmm7,%%xmm6 \n"
|
||||
"neg %4 \n"
|
||||
"lea 0x10(%0,%4,8),%0 \n"
|
||||
"punpckhbw %%xmm7,%%xmm8 \n"
|
||||
"movdqa %%xmm8,%%xmm7 \n"
|
||||
"neg %4 \n"
|
||||
// Second round of bit swap.
|
||||
"movdqa %%xmm0,%%xmm8 \n"
|
||||
"movdqa %%xmm1,%%xmm9 \n"
|
||||
"punpckhwd %%xmm2,%%xmm8 \n"
|
||||
"punpckhwd %%xmm3,%%xmm9 \n"
|
||||
"punpcklwd %%xmm2,%%xmm0 \n"
|
||||
"punpcklwd %%xmm3,%%xmm1 \n"
|
||||
"movdqa %%xmm8,%%xmm2 \n"
|
||||
"movdqa %%xmm9,%%xmm3 \n"
|
||||
"movdqa %%xmm4,%%xmm8 \n"
|
||||
"movdqa %%xmm5,%%xmm9 \n"
|
||||
"punpckhwd %%xmm6,%%xmm8 \n"
|
||||
"punpckhwd %%xmm7,%%xmm9 \n"
|
||||
"punpcklwd %%xmm6,%%xmm4 \n"
|
||||
"punpcklwd %%xmm7,%%xmm5 \n"
|
||||
"movdqa %%xmm8,%%xmm6 \n"
|
||||
"movdqa %%xmm9,%%xmm7 \n"
|
||||
// Third round of bit swap.
|
||||
// Write to the destination pointer.
|
||||
"movdqa %%xmm0,%%xmm8 \n"
|
||||
"punpckldq %%xmm4,%%xmm0 \n"
|
||||
"movlpd %%xmm0,(%1) \n" // Write back U channel
|
||||
"movhpd %%xmm0,(%2) \n" // Write back V channel
|
||||
"punpckhdq %%xmm4,%%xmm8 \n"
|
||||
"movlpd %%xmm8,(%1,%5) \n"
|
||||
"lea (%1,%5,2),%1 \n"
|
||||
"movhpd %%xmm8,(%2,%6) \n"
|
||||
"lea (%2,%6,2),%2 \n"
|
||||
"movdqa %%xmm2,%%xmm8 \n"
|
||||
"punpckldq %%xmm6,%%xmm2 \n"
|
||||
"movlpd %%xmm2,(%1) \n"
|
||||
"movhpd %%xmm2,(%2) \n"
|
||||
"punpckhdq %%xmm6,%%xmm8 \n"
|
||||
"movlpd %%xmm8,(%1,%5) \n"
|
||||
"lea (%1,%5,2),%1 \n"
|
||||
"movhpd %%xmm8,(%2,%6) \n"
|
||||
"lea (%2,%6,2),%2 \n"
|
||||
"movdqa %%xmm1,%%xmm8 \n"
|
||||
"punpckldq %%xmm5,%%xmm1 \n"
|
||||
"movlpd %%xmm1,(%1) \n"
|
||||
"movhpd %%xmm1,(%2) \n"
|
||||
"punpckhdq %%xmm5,%%xmm8 \n"
|
||||
"movlpd %%xmm8,(%1,%5) \n"
|
||||
"lea (%1,%5,2),%1 \n"
|
||||
"movhpd %%xmm8,(%2,%6) \n"
|
||||
"lea (%2,%6,2),%2 \n"
|
||||
"movdqa %%xmm3,%%xmm8 \n"
|
||||
"punpckldq %%xmm7,%%xmm3 \n"
|
||||
"movlpd %%xmm3,(%1) \n"
|
||||
"movhpd %%xmm3,(%2) \n"
|
||||
"punpckhdq %%xmm7,%%xmm8 \n"
|
||||
"sub $0x8,%3 \n"
|
||||
"movlpd %%xmm8,(%1,%5) \n"
|
||||
"lea (%1,%5,2),%1 \n"
|
||||
"movhpd %%xmm8,(%2,%6) \n"
|
||||
"lea (%2,%6,2),%2 \n"
|
||||
"jg 1b \n"
|
||||
: "+r"(src), // %0
|
||||
"+r"(dst_a), // %1
|
||||
"+r"(dst_b), // %2
|
||||
"+r"(width) // %3
|
||||
: "r"((intptr_t) (src_stride)), // %4
|
||||
"r"((intptr_t) (dst_stride_a)), // %5
|
||||
"r"((intptr_t) (dst_stride_b)) // %6
|
||||
: "memory", "cc", "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6",
|
||||
"xmm7", "xmm8", "xmm9");
|
||||
}
|
||||
|
||||
#endif // defined(HAS_TRANSPOSEUVWX8_SSE2)
|
||||
|
||||
#endif // defined(__x86_64__) || defined(__i386__)
|
||||
106
pkg/encoder/yuv/libyuv/rotate_row.h
Normal file
106
pkg/encoder/yuv/libyuv/rotate_row.h
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Copyright 2013 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_LIBYUV_ROTATE_ROW_H_
|
||||
#define INCLUDE_LIBYUV_ROTATE_ROW_H_
|
||||
|
||||
#include "basic_types.h"
|
||||
|
||||
#if defined(__pnacl__) || defined(__CLR_VER) || \
|
||||
(defined(__native_client__) && defined(__x86_64__)) || \
|
||||
(defined(__i386__) && !defined(__SSE__) && !defined(__clang__))
|
||||
#define LIBYUV_DISABLE_X86
|
||||
#endif
|
||||
#if defined(__native_client__)
|
||||
#define LIBYUV_DISABLE_NEON
|
||||
#endif
|
||||
// MemorySanitizer does not support assembly code yet. http://crbug.com/344505
|
||||
#if defined(__has_feature)
|
||||
#if __has_feature(memory_sanitizer) && !defined(LIBYUV_DISABLE_NEON)
|
||||
#define LIBYUV_DISABLE_NEON
|
||||
#endif
|
||||
#if __has_feature(memory_sanitizer) && !defined(LIBYUV_DISABLE_X86)
|
||||
#define LIBYUV_DISABLE_X86
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// The following are available for GCC 32 or 64 bit:
|
||||
#if !defined(LIBYUV_DISABLE_X86) && (defined(__i386__) || defined(__x86_64__))
|
||||
#define HAS_TRANSPOSEWX8_SSSE3
|
||||
#endif
|
||||
|
||||
// The following are available for 64 bit GCC:
|
||||
#if !defined(LIBYUV_DISABLE_X86) && defined(__x86_64__)
|
||||
#define HAS_TRANSPOSEWX8_FAST_SSSE3
|
||||
#define HAS_TRANSPOSEUVWX8_SSE2
|
||||
#endif
|
||||
|
||||
void TransposeWxH_C(const uint8_t *src,
|
||||
int src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_stride,
|
||||
int width,
|
||||
int height);
|
||||
|
||||
void TransposeWx8_C(const uint8_t *src,
|
||||
int src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_stride,
|
||||
int width);
|
||||
|
||||
void TransposeWx8_SSSE3(const uint8_t *src,
|
||||
int src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_stride,
|
||||
int width);
|
||||
|
||||
void TransposeWx8_Fast_SSSE3(const uint8_t *src,
|
||||
int src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_stride,
|
||||
int width);
|
||||
|
||||
void TransposeWx8_Any_SSSE3(const uint8_t *src,
|
||||
int src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_stride,
|
||||
int width);
|
||||
|
||||
void TransposeWx8_Fast_Any_SSSE3(const uint8_t *src,
|
||||
int src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_stride,
|
||||
int width);
|
||||
|
||||
void TransposeUVWx8_C(const uint8_t *src,
|
||||
int src_stride,
|
||||
uint8_t *dst_a,
|
||||
int dst_stride_a,
|
||||
uint8_t *dst_b,
|
||||
int dst_stride_b,
|
||||
int width);
|
||||
|
||||
void TransposeUVWx8_SSE2(const uint8_t *src,
|
||||
int src_stride,
|
||||
uint8_t *dst_a,
|
||||
int dst_stride_a,
|
||||
uint8_t *dst_b,
|
||||
int dst_stride_b,
|
||||
int width);
|
||||
|
||||
void TransposeUVWx8_Any_SSE2(const uint8_t *src,
|
||||
int src_stride,
|
||||
uint8_t *dst_a,
|
||||
int dst_stride_a,
|
||||
uint8_t *dst_b,
|
||||
int dst_stride_b,
|
||||
int width);
|
||||
|
||||
#endif // INCLUDE_LIBYUV_ROTATE_ROW_H_
|
||||
426
pkg/encoder/yuv/libyuv/row.h
Normal file
426
pkg/encoder/yuv/libyuv/row.h
Normal file
|
|
@ -0,0 +1,426 @@
|
|||
/*
|
||||
* Copyright 2011 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_LIBYUV_ROW_H_
|
||||
#define INCLUDE_LIBYUV_ROW_H_
|
||||
|
||||
#include <stddef.h> // For NULL
|
||||
#include <stdlib.h> // For malloc
|
||||
|
||||
#include "basic_types.h"
|
||||
|
||||
#if defined(__pnacl__) || defined(__CLR_VER) || \
|
||||
(defined(__native_client__) && defined(__x86_64__)) || \
|
||||
(defined(__i386__) && !defined(__SSE__) && !defined(__clang__))
|
||||
#define LIBYUV_DISABLE_X86
|
||||
#endif
|
||||
#if defined(__native_client__)
|
||||
#define LIBYUV_DISABLE_NEON
|
||||
#endif
|
||||
// MemorySanitizer does not support assembly code yet. http://crbug.com/344505
|
||||
#if defined(__has_feature)
|
||||
#if __has_feature(memory_sanitizer) && !defined(LIBYUV_DISABLE_NEON)
|
||||
#define LIBYUV_DISABLE_NEON
|
||||
#endif
|
||||
#if __has_feature(memory_sanitizer) && !defined(LIBYUV_DISABLE_X86)
|
||||
#define LIBYUV_DISABLE_X86
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// GCC >= 4.7.0 required for AVX2.
|
||||
#if defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__))
|
||||
#if (__GNUC__ > 4) || (__GNUC__ == 4 && (__GNUC_MINOR__ >= 7))
|
||||
#define GCC_HAS_AVX2 1
|
||||
#endif // GNUC >= 4.7
|
||||
#endif // __GNUC__
|
||||
|
||||
// The following are available on all x86 platforms:
|
||||
#if !defined(LIBYUV_DISABLE_X86) && \
|
||||
(defined(_M_IX86) || defined(__x86_64__) || defined(__i386__))
|
||||
// Conversions:
|
||||
#define HAS_ABGRTOYROW_SSSE3
|
||||
#define HAS_ARGBTOYROW_SSSE3
|
||||
#define HAS_BGRATOYROW_SSSE3
|
||||
#define HAS_COPYROW_ERMS
|
||||
#define HAS_COPYROW_SSE2
|
||||
#define HAS_INTERPOLATEROW_SSSE3
|
||||
#define HAS_MIRRORROW_SSSE3
|
||||
#define HAS_MIRRORSPLITUVROW_SSSE3
|
||||
#if !defined(LIBYUV_BIT_EXACT)
|
||||
#define HAS_ABGRTOUVROW_SSSE3
|
||||
#define HAS_ARGBTOUVROW_SSSE3
|
||||
#endif
|
||||
|
||||
// Effects:
|
||||
#define HAS_ARGBGRAYROW_SSSE3
|
||||
#define HAS_ARGBMIRRORROW_SSE2
|
||||
|
||||
#endif
|
||||
|
||||
// The following are available on all x86 platforms, but
|
||||
// require VS2012, clang 3.4 or gcc 4.7.
|
||||
#if !defined(LIBYUV_DISABLE_X86) && \
|
||||
(defined(VISUALC_HAS_AVX2) || defined(CLANG_HAS_AVX2) || \
|
||||
defined(GCC_HAS_AVX2))
|
||||
#define HAS_ARGBEXTRACTALPHAROW_AVX2
|
||||
#define HAS_ARGBMIRRORROW_AVX2
|
||||
#define HAS_ARGBTOYROW_AVX2
|
||||
#define HAS_COPYROW_AVX
|
||||
#define HAS_INTERPOLATEROW_AVX2
|
||||
#define HAS_MIRRORROW_AVX2
|
||||
#if !defined(LIBYUV_BIT_EXACT)
|
||||
#define HAS_ARGBTOUVROW_AVX2
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
// The following are available for gcc/clang x86 platforms:
|
||||
// TODO(fbarchard): Port to Visual C
|
||||
#if !defined(LIBYUV_DISABLE_X86) && (defined(__x86_64__) || defined(__i386__))
|
||||
#define HAS_MIRRORUVROW_SSSE3
|
||||
|
||||
#endif
|
||||
|
||||
// The following are available for AVX2 gcc/clang x86 platforms:
|
||||
// TODO(fbarchard): Port to Visual C
|
||||
#if !defined(LIBYUV_DISABLE_X86) && \
|
||||
(defined(__x86_64__) || defined(__i386__)) && \
|
||||
(defined(CLANG_HAS_AVX2) || defined(GCC_HAS_AVX2))
|
||||
#define HAS_ABGRTOYROW_AVX2
|
||||
#define HAS_MIRRORUVROW_AVX2
|
||||
#if !defined(LIBYUV_BIT_EXACT)
|
||||
#define HAS_ABGRTOUVROW_AVX2
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__CLR_VER) && !defined(__clang__)
|
||||
#if defined(VISUALC_HAS_AVX2)
|
||||
#define SIMD_ALIGNED(var) __declspec(align(32)) var
|
||||
#else
|
||||
#define SIMD_ALIGNED(var) __declspec(align(16)) var
|
||||
#endif
|
||||
#define LIBYUV_NOINLINE __declspec(noinline)
|
||||
typedef __declspec(align(16)) int16_t vec16[8];
|
||||
typedef __declspec(align(16)) int32_t vec32[4];
|
||||
typedef __declspec(align(16)) float vecf32[4];
|
||||
typedef __declspec(align(16)) int8_t vec8[16];
|
||||
typedef __declspec(align(16)) uint16_t uvec16[8];
|
||||
typedef __declspec(align(16)) uint32_t uvec32[4];
|
||||
typedef __declspec(align(16)) uint8_t uvec8[16];
|
||||
typedef __declspec(align(32)) int16_t lvec16[16];
|
||||
typedef __declspec(align(32)) int32_t lvec32[8];
|
||||
typedef __declspec(align(32)) int8_t lvec8[32];
|
||||
typedef __declspec(align(32)) uint16_t ulvec16[16];
|
||||
typedef __declspec(align(32)) uint32_t ulvec32[8];
|
||||
typedef __declspec(align(32)) uint8_t ulvec8[32];
|
||||
#elif !defined(__pnacl__) && (defined(__GNUC__) || defined(__clang__))
|
||||
// Caveat GCC 4.2 to 4.7 have a known issue using vectors with const.
|
||||
#if defined(CLANG_HAS_AVX2) || defined(GCC_HAS_AVX2)
|
||||
#define SIMD_ALIGNED(var) var __attribute__((aligned(32)))
|
||||
#else
|
||||
#define SIMD_ALIGNED(var) var __attribute__((aligned(16)))
|
||||
#endif
|
||||
#define LIBYUV_NOINLINE __attribute__((noinline))
|
||||
typedef int16_t __attribute__((vector_size(16))) vec16;
|
||||
typedef int32_t __attribute__((vector_size(16))) vec32;
|
||||
typedef float __attribute__((vector_size(16))) vecf32;
|
||||
typedef int8_t __attribute__((vector_size(16))) vec8;
|
||||
typedef uint16_t __attribute__((vector_size(16))) uvec16;
|
||||
typedef uint32_t __attribute__((vector_size(16))) uvec32;
|
||||
typedef uint8_t __attribute__((vector_size(16))) uvec8;
|
||||
typedef int16_t __attribute__((vector_size(32))) lvec16;
|
||||
typedef int32_t __attribute__((vector_size(32))) lvec32;
|
||||
typedef int8_t __attribute__((vector_size(32))) lvec8;
|
||||
typedef uint16_t __attribute__((vector_size(32))) ulvec16;
|
||||
typedef uint32_t __attribute__((vector_size(32))) ulvec32;
|
||||
typedef uint8_t __attribute__((vector_size(32))) ulvec8;
|
||||
#else
|
||||
#define SIMD_ALIGNED(var) var
|
||||
#define LIBYUV_NOINLINE
|
||||
typedef int16_t vec16[8];
|
||||
typedef int32_t vec32[4];
|
||||
typedef float vecf32[4];
|
||||
typedef int8_t vec8[16];
|
||||
typedef uint16_t uvec16[8];
|
||||
typedef uint32_t uvec32[4];
|
||||
typedef uint8_t uvec8[16];
|
||||
typedef int16_t lvec16[16];
|
||||
typedef int32_t lvec32[8];
|
||||
typedef int8_t lvec8[32];
|
||||
typedef uint16_t ulvec16[16];
|
||||
typedef uint32_t ulvec32[8];
|
||||
typedef uint8_t ulvec8[32];
|
||||
#endif
|
||||
|
||||
#if !defined(__aarch64__) || !defined(__arm__)
|
||||
// This struct is for Intel color conversion.
|
||||
struct YuvConstants {
|
||||
uint8_t kUVToB[32];
|
||||
uint8_t kUVToG[32];
|
||||
uint8_t kUVToR[32];
|
||||
int16_t kYToRgb[16];
|
||||
int16_t kYBiasToRgb[16];
|
||||
};
|
||||
#endif
|
||||
|
||||
#define IS_ALIGNED(p, a) (!((uintptr_t)(p) & ((a)-1)))
|
||||
|
||||
#define align_buffer_64(var, size) \
|
||||
void* var##_mem = malloc((size) + 63); /* NOLINT */ \
|
||||
uint8_t* var = (uint8_t*)(((intptr_t)var##_mem + 63) & ~63) /* NOLINT */
|
||||
|
||||
#define free_aligned_buffer_64(var) \
|
||||
free(var##_mem); \
|
||||
var = NULL
|
||||
|
||||
#if defined(__APPLE__) || defined(__x86_64__) || defined(__llvm__)
|
||||
#define OMITFP
|
||||
#else
|
||||
#define OMITFP __attribute__((optimize("omit-frame-pointer")))
|
||||
#endif
|
||||
|
||||
// NaCL macros for GCC x86 and x64.
|
||||
#if defined(__native_client__)
|
||||
#define LABELALIGN ".p2align 5\n"
|
||||
#else
|
||||
#define LABELALIGN
|
||||
#endif
|
||||
|
||||
void ARGBToYRow_AVX2(const uint8_t *src_argb, uint8_t *dst_y, int width);
|
||||
|
||||
void ARGBToYRow_Any_AVX2(const uint8_t *src_ptr, uint8_t *dst_ptr, int width);
|
||||
|
||||
void ABGRToYRow_AVX2(const uint8_t *src_abgr, uint8_t *dst_y, int width);
|
||||
|
||||
void ABGRToYRow_Any_AVX2(const uint8_t *src_ptr, uint8_t *dst_ptr, int width);
|
||||
|
||||
void ARGBToYRow_SSSE3(const uint8_t *src_argb, uint8_t *dst_y, int width);
|
||||
|
||||
void ABGRToYRow_SSSE3(const uint8_t *src_abgr, uint8_t *dst_y, int width);
|
||||
|
||||
void BGRAToYRow_SSSE3(const uint8_t *src_bgra, uint8_t *dst_y, int width);
|
||||
|
||||
void ABGRToYRow_SSSE3(const uint8_t *src_abgr, uint8_t *dst_y, int width);
|
||||
|
||||
void ARGBToYRow_C(const uint8_t *src_rgb, uint8_t *dst_y, int width);
|
||||
|
||||
void ABGRToYRow_C(const uint8_t *src_rgb, uint8_t *dst_y, int width);
|
||||
|
||||
void RGB565ToYRow_C(const uint8_t *src_rgb565, uint8_t *dst_y, int width);
|
||||
|
||||
void ARGBToYRow_Any_SSSE3(const uint8_t *src_ptr, uint8_t *dst_ptr, int width);
|
||||
|
||||
void BGRAToYRow_Any_SSSE3(const uint8_t *src_ptr, uint8_t *dst_ptr, int width);
|
||||
|
||||
void ABGRToYRow_Any_SSSE3(const uint8_t *src_ptr, uint8_t *dst_ptr, int width);
|
||||
|
||||
void ARGBToUVRow_AVX2(const uint8_t *src_argb,
|
||||
int src_stride_argb,
|
||||
uint8_t *dst_u,
|
||||
uint8_t *dst_v,
|
||||
int width);
|
||||
|
||||
void ABGRToUVRow_AVX2(const uint8_t *src_abgr,
|
||||
int src_stride_abgr,
|
||||
uint8_t *dst_u,
|
||||
uint8_t *dst_v,
|
||||
int width);
|
||||
|
||||
void ARGBToUVRow_SSSE3(const uint8_t *src_argb,
|
||||
int src_stride_argb,
|
||||
uint8_t *dst_u,
|
||||
uint8_t *dst_v,
|
||||
int width);
|
||||
|
||||
void BGRAToUVRow_SSSE3(const uint8_t *src_bgra,
|
||||
int src_stride_bgra,
|
||||
uint8_t *dst_u,
|
||||
uint8_t *dst_v,
|
||||
int width);
|
||||
|
||||
void ABGRToUVRow_SSSE3(const uint8_t *src_abgr,
|
||||
int src_stride_abgr,
|
||||
uint8_t *dst_u,
|
||||
uint8_t *dst_v,
|
||||
int width);
|
||||
|
||||
void RGBAToUVRow_SSSE3(const uint8_t *src_rgba,
|
||||
int src_stride_rgba,
|
||||
uint8_t *dst_u,
|
||||
uint8_t *dst_v,
|
||||
int width);
|
||||
|
||||
void ARGBToUVRow_Any_AVX2(const uint8_t *src_ptr,
|
||||
int src_stride,
|
||||
uint8_t *dst_u,
|
||||
uint8_t *dst_v,
|
||||
int width);
|
||||
|
||||
void ABGRToUVRow_Any_AVX2(const uint8_t *src_ptr,
|
||||
int src_stride,
|
||||
uint8_t *dst_u,
|
||||
uint8_t *dst_v,
|
||||
int width);
|
||||
|
||||
void ARGBToUVRow_Any_SSSE3(const uint8_t *src_ptr,
|
||||
int src_stride,
|
||||
uint8_t *dst_u,
|
||||
uint8_t *dst_v,
|
||||
int width);
|
||||
|
||||
void BGRAToUVRow_Any_SSSE3(const uint8_t *src_ptr,
|
||||
int src_stride,
|
||||
uint8_t *dst_u,
|
||||
uint8_t *dst_v,
|
||||
int width);
|
||||
|
||||
void ABGRToUVRow_Any_SSSE3(const uint8_t *src_ptr,
|
||||
int src_stride,
|
||||
uint8_t *dst_u,
|
||||
uint8_t *dst_v,
|
||||
int width);
|
||||
|
||||
void RGBAToUVRow_Any_SSSE3(const uint8_t *src_ptr,
|
||||
int src_stride,
|
||||
uint8_t *dst_u,
|
||||
uint8_t *dst_v,
|
||||
int width);
|
||||
|
||||
void ARGBToUVRow_C(const uint8_t *src_rgb,
|
||||
int src_stride_rgb,
|
||||
uint8_t *dst_u,
|
||||
uint8_t *dst_v,
|
||||
int width);
|
||||
|
||||
void ARGBToUVRow_C(const uint8_t *src_rgb,
|
||||
int src_stride_rgb,
|
||||
uint8_t *dst_u,
|
||||
uint8_t *dst_v,
|
||||
int width);
|
||||
|
||||
void BGRAToUVRow_C(const uint8_t *src_rgb,
|
||||
int src_stride_rgb,
|
||||
uint8_t *dst_u,
|
||||
uint8_t *dst_v,
|
||||
int width);
|
||||
|
||||
void ABGRToUVRow_C(const uint8_t *src_rgb,
|
||||
int src_stride_rgb,
|
||||
uint8_t *dst_u,
|
||||
uint8_t *dst_v,
|
||||
int width);
|
||||
|
||||
void RGBAToUVRow_C(const uint8_t *src_rgb,
|
||||
int src_stride_rgb,
|
||||
uint8_t *dst_u,
|
||||
uint8_t *dst_v,
|
||||
int width);
|
||||
|
||||
void RGB565ToUVRow_C(const uint8_t *src_rgb565,
|
||||
int src_stride_rgb565,
|
||||
uint8_t *dst_u,
|
||||
uint8_t *dst_v,
|
||||
int width);
|
||||
|
||||
void MirrorRow_AVX2(const uint8_t *src, uint8_t *dst, int width);
|
||||
|
||||
void MirrorRow_SSSE3(const uint8_t *src, uint8_t *dst, int width);
|
||||
|
||||
void MirrorRow_C(const uint8_t *src, uint8_t *dst, int width);
|
||||
|
||||
void MirrorRow_Any_AVX2(const uint8_t *src_ptr, uint8_t *dst_ptr, int width);
|
||||
|
||||
void MirrorRow_Any_SSSE3(const uint8_t *src_ptr, uint8_t *dst_ptr, int width);
|
||||
|
||||
void MirrorRow_Any_SSE2(const uint8_t *src, uint8_t *dst, int width);
|
||||
|
||||
void MirrorUVRow_AVX2(const uint8_t *src_uv, uint8_t *dst_uv, int width);
|
||||
|
||||
void MirrorUVRow_SSSE3(const uint8_t *src_uv, uint8_t *dst_uv, int width);
|
||||
|
||||
void MirrorUVRow_Any_AVX2(const uint8_t *src_ptr, uint8_t *dst_ptr, int width);
|
||||
|
||||
void MirrorUVRow_Any_SSSE3(const uint8_t *src_ptr, uint8_t *dst_ptr, int width);
|
||||
|
||||
void ARGBMirrorRow_AVX2(const uint8_t *src, uint8_t *dst, int width);
|
||||
|
||||
void ARGBMirrorRow_SSE2(const uint8_t *src, uint8_t *dst, int width);
|
||||
|
||||
void ARGBMirrorRow_C(const uint8_t *src, uint8_t *dst, int width);
|
||||
|
||||
void ARGBMirrorRow_Any_AVX2(const uint8_t *src_ptr,
|
||||
uint8_t *dst_ptr,
|
||||
int width);
|
||||
|
||||
void ARGBMirrorRow_Any_SSE2(const uint8_t *src_ptr,
|
||||
uint8_t *dst_ptr,
|
||||
int width);
|
||||
|
||||
void CopyRow_SSE2(const uint8_t *src, uint8_t *dst, int width);
|
||||
|
||||
void CopyRow_AVX(const uint8_t *src, uint8_t *dst, int width);
|
||||
|
||||
void CopyRow_ERMS(const uint8_t *src, uint8_t *dst, int width);
|
||||
|
||||
void CopyRow_C(const uint8_t *src, uint8_t *dst, int count);
|
||||
|
||||
void CopyRow_Any_SSE2(const uint8_t *src_ptr, uint8_t *dst_ptr, int width);
|
||||
|
||||
void CopyRow_Any_AVX(const uint8_t *src_ptr, uint8_t *dst_ptr, int width);
|
||||
|
||||
void RGB565ToARGBRow_SSE2(const uint8_t *src, uint8_t *dst, int width);
|
||||
|
||||
void RGB565ToARGBRow_AVX2(const uint8_t *src_rgb565,
|
||||
uint8_t *dst_argb,
|
||||
int width);
|
||||
|
||||
void RGB565ToARGBRow_C(const uint8_t *src_rgb565, uint8_t *dst_argb, int width);
|
||||
|
||||
void RGB565ToARGBRow_Any_SSE2(const uint8_t *src_ptr,
|
||||
uint8_t *dst_ptr,
|
||||
int width);
|
||||
|
||||
void RGB565ToARGBRow_Any_AVX2(const uint8_t *src_ptr,
|
||||
uint8_t *dst_ptr,
|
||||
int width);
|
||||
|
||||
// Used for I420Scale, ARGBScale, and ARGBInterpolate.
|
||||
void InterpolateRow_C(uint8_t *dst_ptr,
|
||||
const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
int width,
|
||||
int source_y_fraction);
|
||||
|
||||
void InterpolateRow_SSSE3(uint8_t *dst_ptr,
|
||||
const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
int dst_width,
|
||||
int source_y_fraction);
|
||||
|
||||
void InterpolateRow_AVX2(uint8_t *dst_ptr,
|
||||
const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
int dst_width,
|
||||
int source_y_fraction);
|
||||
|
||||
void InterpolateRow_Any_SSSE3(uint8_t *dst_ptr,
|
||||
const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride_ptr,
|
||||
int width,
|
||||
int source_y_fraction);
|
||||
|
||||
void InterpolateRow_Any_AVX2(uint8_t *dst_ptr,
|
||||
const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride_ptr,
|
||||
int width,
|
||||
int source_y_fraction);
|
||||
|
||||
#endif // INCLUDE_LIBYUV_ROW_H_
|
||||
206
pkg/encoder/yuv/libyuv/row_any.c
Normal file
206
pkg/encoder/yuv/libyuv/row_any.c
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
/*
|
||||
* Copyright 2012 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "row.h"
|
||||
|
||||
#include <string.h> // For memset.
|
||||
|
||||
// Subsampled source needs to be increase by 1 of not even.
|
||||
#define SS(width, shift) (((width) + (1 << (shift)) - 1) >> (shift))
|
||||
|
||||
// Any 1 to 1.
|
||||
#define ANY11(NAMEANY, ANY_SIMD, UVSHIFT, SBPP, BPP, MASK) \
|
||||
void NAMEANY(const uint8_t* src_ptr, uint8_t* dst_ptr, int width) { \
|
||||
SIMD_ALIGNED(uint8_t vin[128]); \
|
||||
SIMD_ALIGNED(uint8_t vout[128]); \
|
||||
memset(vin, 0, sizeof(vin)); /* for YUY2 and msan */ \
|
||||
int r = width & MASK; \
|
||||
int n = width & ~MASK; \
|
||||
if (n > 0) { \
|
||||
ANY_SIMD(src_ptr, dst_ptr, n); \
|
||||
} \
|
||||
memcpy(vin, src_ptr + (n >> UVSHIFT) * SBPP, SS(r, UVSHIFT) * SBPP); \
|
||||
ANY_SIMD(vin, vout, MASK + 1); \
|
||||
memcpy(dst_ptr + n * BPP, vout, r * BPP); \
|
||||
}
|
||||
|
||||
#ifdef HAS_COPYROW_AVX
|
||||
|
||||
ANY11(CopyRow_Any_AVX, CopyRow_AVX, 0, 1, 1, 63)
|
||||
|
||||
#endif
|
||||
#ifdef HAS_COPYROW_SSE2
|
||||
|
||||
ANY11(CopyRow_Any_SSE2, CopyRow_SSE2, 0, 1, 1, 31)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAS_ARGBTOYROW_AVX2
|
||||
|
||||
ANY11(ARGBToYRow_Any_AVX2, ARGBToYRow_AVX2, 0, 4, 1, 31)
|
||||
|
||||
#endif
|
||||
#ifdef HAS_ABGRTOYROW_AVX2
|
||||
|
||||
ANY11(ABGRToYRow_Any_AVX2, ABGRToYRow_AVX2, 0, 4, 1, 31)
|
||||
|
||||
#endif
|
||||
#ifdef HAS_ARGBTOYROW_SSSE3
|
||||
|
||||
ANY11(ARGBToYRow_Any_SSSE3, ARGBToYRow_SSSE3, 0, 4, 1, 15)
|
||||
|
||||
#endif
|
||||
#ifdef HAS_BGRATOYROW_SSSE3
|
||||
|
||||
ANY11(BGRAToYRow_Any_SSSE3, BGRAToYRow_SSSE3, 0, 4, 1, 15)
|
||||
|
||||
ANY11(ABGRToYRow_Any_SSSE3, ABGRToYRow_SSSE3, 0, 4, 1, 15)
|
||||
|
||||
#endif
|
||||
|
||||
#undef ANY11
|
||||
|
||||
// Any 1 to 1 interpolate. Takes 2 rows of source via stride.
|
||||
#define ANY11I(NAMEANY, ANY_SIMD, TD, TS, SBPP, BPP, MASK) \
|
||||
void NAMEANY(TD* dst_ptr, const TS* src_ptr, ptrdiff_t src_stride, \
|
||||
int width, int source_y_fraction) { \
|
||||
SIMD_ALIGNED(TS vin[64 * 2]); \
|
||||
SIMD_ALIGNED(TD vout[64]); \
|
||||
memset(vin, 0, sizeof(vin)); /* for msan */ \
|
||||
int r = width & MASK; \
|
||||
int n = width & ~MASK; \
|
||||
if (n > 0) { \
|
||||
ANY_SIMD(dst_ptr, src_ptr, src_stride, n, source_y_fraction); \
|
||||
} \
|
||||
memcpy(vin, src_ptr + n * SBPP, r * SBPP * sizeof(TS)); \
|
||||
if (source_y_fraction) { \
|
||||
memcpy(vin + 64, src_ptr + src_stride + n * SBPP, \
|
||||
r * SBPP * sizeof(TS)); \
|
||||
} \
|
||||
ANY_SIMD(vout, vin, 64, MASK + 1, source_y_fraction); \
|
||||
memcpy(dst_ptr + n * BPP, vout, r * BPP * sizeof(TD)); \
|
||||
}
|
||||
|
||||
#ifdef HAS_INTERPOLATEROW_AVX2
|
||||
|
||||
ANY11I(InterpolateRow_Any_AVX2, InterpolateRow_AVX2, uint8_t, uint8_t, 1, 1, 31)
|
||||
|
||||
#endif
|
||||
#ifdef HAS_INTERPOLATEROW_SSSE3
|
||||
|
||||
ANY11I(InterpolateRow_Any_SSSE3,
|
||||
InterpolateRow_SSSE3,
|
||||
uint8_t,
|
||||
uint8_t,
|
||||
1,
|
||||
1,
|
||||
15)
|
||||
|
||||
#endif
|
||||
|
||||
#undef ANY11I
|
||||
|
||||
// Any 1 to 1 mirror.
|
||||
#define ANY11M(NAMEANY, ANY_SIMD, BPP, MASK) \
|
||||
void NAMEANY(const uint8_t* src_ptr, uint8_t* dst_ptr, int width) { \
|
||||
SIMD_ALIGNED(uint8_t vin[64]); \
|
||||
SIMD_ALIGNED(uint8_t vout[64]); \
|
||||
memset(vin, 0, sizeof(vin)); /* for msan */ \
|
||||
int r = width & MASK; \
|
||||
int n = width & ~MASK; \
|
||||
if (n > 0) { \
|
||||
ANY_SIMD(src_ptr + r * BPP, dst_ptr, n); \
|
||||
} \
|
||||
memcpy(vin, src_ptr, r* BPP); \
|
||||
ANY_SIMD(vin, vout, MASK + 1); \
|
||||
memcpy(dst_ptr + n * BPP, vout + (MASK + 1 - r) * BPP, r * BPP); \
|
||||
}
|
||||
|
||||
#ifdef HAS_MIRRORROW_AVX2
|
||||
|
||||
ANY11M(MirrorRow_Any_AVX2, MirrorRow_AVX2, 1, 31)
|
||||
|
||||
#endif
|
||||
#ifdef HAS_MIRRORROW_SSSE3
|
||||
|
||||
ANY11M(MirrorRow_Any_SSSE3, MirrorRow_SSSE3, 1, 15)
|
||||
|
||||
#endif
|
||||
#ifdef HAS_MIRRORUVROW_AVX2
|
||||
|
||||
ANY11M(MirrorUVRow_Any_AVX2, MirrorUVRow_AVX2, 2, 15)
|
||||
|
||||
#endif
|
||||
#ifdef HAS_MIRRORUVROW_SSSE3
|
||||
|
||||
ANY11M(MirrorUVRow_Any_SSSE3, MirrorUVRow_SSSE3, 2, 7)
|
||||
|
||||
#endif
|
||||
#ifdef HAS_ARGBMIRRORROW_AVX2
|
||||
|
||||
ANY11M(ARGBMirrorRow_Any_AVX2, ARGBMirrorRow_AVX2, 4, 7)
|
||||
|
||||
#endif
|
||||
#ifdef HAS_ARGBMIRRORROW_SSE2
|
||||
|
||||
ANY11M(ARGBMirrorRow_Any_SSE2, ARGBMirrorRow_SSE2, 4, 3)
|
||||
|
||||
#endif
|
||||
#undef ANY11M
|
||||
|
||||
// Any 1 to 2 with source stride (2 rows of source). Outputs UV planes.
|
||||
// 128 byte row allows for 32 avx ARGB pixels.
|
||||
#define ANY12S(NAMEANY, ANY_SIMD, UVSHIFT, BPP, MASK) \
|
||||
void NAMEANY(const uint8_t* src_ptr, int src_stride, uint8_t* dst_u, \
|
||||
uint8_t* dst_v, int width) { \
|
||||
SIMD_ALIGNED(uint8_t vin[128 * 2]); \
|
||||
SIMD_ALIGNED(uint8_t vout[128 * 2]); \
|
||||
memset(vin, 0, sizeof(vin)); /* for msan */ \
|
||||
int r = width & MASK; \
|
||||
int n = width & ~MASK; \
|
||||
if (n > 0) { \
|
||||
ANY_SIMD(src_ptr, src_stride, dst_u, dst_v, n); \
|
||||
} \
|
||||
memcpy(vin, src_ptr + (n >> UVSHIFT) * BPP, SS(r, UVSHIFT) * BPP); \
|
||||
memcpy(vin + 128, src_ptr + src_stride + (n >> UVSHIFT) * BPP, \
|
||||
SS(r, UVSHIFT) * BPP); \
|
||||
if ((width & 1) && UVSHIFT == 0) { /* repeat last pixel for subsample */ \
|
||||
memcpy(vin + SS(r, UVSHIFT) * BPP, vin + SS(r, UVSHIFT) * BPP - BPP, \
|
||||
BPP); \
|
||||
memcpy(vin + 128 + SS(r, UVSHIFT) * BPP, \
|
||||
vin + 128 + SS(r, UVSHIFT) * BPP - BPP, BPP); \
|
||||
} \
|
||||
ANY_SIMD(vin, 128, vout, vout + 128, MASK + 1); \
|
||||
memcpy(dst_u + (n >> 1), vout, SS(r, 1)); \
|
||||
memcpy(dst_v + (n >> 1), vout + 128, SS(r, 1)); \
|
||||
}
|
||||
|
||||
#ifdef HAS_ARGBTOUVROW_AVX2
|
||||
|
||||
ANY12S(ARGBToUVRow_Any_AVX2, ARGBToUVRow_AVX2, 0, 4, 31)
|
||||
|
||||
#endif
|
||||
#ifdef HAS_ABGRTOUVROW_AVX2
|
||||
|
||||
ANY12S(ABGRToUVRow_Any_AVX2, ABGRToUVRow_AVX2, 0, 4, 31)
|
||||
|
||||
#endif
|
||||
#ifdef HAS_ARGBTOUVROW_SSSE3
|
||||
|
||||
ANY12S(ARGBToUVRow_Any_SSSE3, ARGBToUVRow_SSSE3, 0, 4, 15)
|
||||
|
||||
ANY12S(BGRAToUVRow_Any_SSSE3, BGRAToUVRow_SSSE3, 0, 4, 15)
|
||||
|
||||
ANY12S(ABGRToUVRow_Any_SSSE3, ABGRToUVRow_SSSE3, 0, 4, 15)
|
||||
|
||||
ANY12S(RGBAToUVRow_Any_SSSE3, RGBAToUVRow_SSSE3, 0, 4, 15)
|
||||
|
||||
#endif
|
||||
#undef ANY12S
|
||||
887
pkg/encoder/yuv/libyuv/row_common.c
Normal file
887
pkg/encoder/yuv/libyuv/row_common.c
Normal file
|
|
@ -0,0 +1,887 @@
|
|||
/*
|
||||
* Copyright 2011 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "row.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h> // For memcpy and memset.
|
||||
|
||||
#define STATIC_CAST(type, expr) (type)(expr)
|
||||
|
||||
// This macro controls YUV to RGB using unsigned math to extend range of
|
||||
// YUV to RGB coefficients to 0 to 4 instead of 0 to 2 for more accuracy on B:
|
||||
// LIBYUV_UNLIMITED_DATA
|
||||
|
||||
// Macros to enable unlimited data for each colorspace
|
||||
// LIBYUV_UNLIMITED_BT601
|
||||
// LIBYUV_UNLIMITED_BT709
|
||||
// LIBYUV_UNLIMITED_BT2020
|
||||
|
||||
#if !defined(LIBYUV_BIT_EXACT) && (defined(__x86_64__) || defined(_M_X64) || \
|
||||
defined(__i386__) || defined(_M_IX86))
|
||||
#define LIBYUV_ARGBTOUV_PAVGB 1
|
||||
#define LIBYUV_RGBTOU_TRUNCATE 1
|
||||
#endif
|
||||
#if defined(LIBYUV_BIT_EXACT)
|
||||
#define LIBYUV_UNATTENUATE_DUP 1
|
||||
#endif
|
||||
|
||||
// llvm x86 is poor at ternary operator, so use branchless min/max.
|
||||
|
||||
#define USE_BRANCHLESS 1
|
||||
#if USE_BRANCHLESS
|
||||
|
||||
static __inline int32_t clamp0(int32_t v) {
|
||||
return -(v >= 0) & v;
|
||||
}
|
||||
|
||||
// TODO(fbarchard): make clamp255 preserve negative values.
|
||||
static __inline int32_t clamp255(int32_t v) {
|
||||
return (-(v >= 255) | v) & 255;
|
||||
}
|
||||
|
||||
static __inline int32_t clamp1023(int32_t v) {
|
||||
return (-(v >= 1023) | v) & 1023;
|
||||
}
|
||||
|
||||
// clamp to max
|
||||
static __inline int32_t ClampMax(int32_t v, int32_t max) {
|
||||
return (-(v >= max) | v) & max;
|
||||
}
|
||||
|
||||
static __inline uint32_t Abs(int32_t v) {
|
||||
int m = -(v < 0);
|
||||
return (v + m) ^ m;
|
||||
}
|
||||
|
||||
#else // USE_BRANCHLESS
|
||||
static __inline int32_t clamp0(int32_t v) {
|
||||
return (v < 0) ? 0 : v;
|
||||
}
|
||||
|
||||
static __inline int32_t clamp255(int32_t v) {
|
||||
return (v > 255) ? 255 : v;
|
||||
}
|
||||
|
||||
static __inline int32_t clamp1023(int32_t v) {
|
||||
return (v > 1023) ? 1023 : v;
|
||||
}
|
||||
|
||||
static __inline int32_t ClampMax(int32_t v, int32_t max) {
|
||||
return (v > max) ? max : v;
|
||||
}
|
||||
|
||||
static __inline uint32_t Abs(int32_t v) {
|
||||
return (v < 0) ? -v : v;
|
||||
}
|
||||
#endif // USE_BRANCHLESS
|
||||
|
||||
static __inline uint32_t Clamp(int32_t val) {
|
||||
int v = clamp0(val);
|
||||
return (uint32_t) (clamp255(v));
|
||||
}
|
||||
|
||||
static __inline uint32_t Clamp10(int32_t val) {
|
||||
int v = clamp0(val);
|
||||
return (uint32_t) (clamp1023(v));
|
||||
}
|
||||
|
||||
// Little Endian
|
||||
#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || \
|
||||
defined(_M_IX86) || defined(__arm__) || defined(_M_ARM) || \
|
||||
(defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
|
||||
#define WRITEWORD(p, v) *(uint32_t*)(p) = v
|
||||
#else
|
||||
static inline void WRITEWORD(uint8_t* p, uint32_t v) {
|
||||
p[0] = (uint8_t)(v & 255);
|
||||
p[1] = (uint8_t)((v >> 8) & 255);
|
||||
p[2] = (uint8_t)((v >> 16) & 255);
|
||||
p[3] = (uint8_t)((v >> 24) & 255);
|
||||
}
|
||||
#endif
|
||||
|
||||
void RGB565ToARGBRow_C(const uint8_t *src_rgb565,
|
||||
uint8_t *dst_argb,
|
||||
int width) {
|
||||
int x;
|
||||
for (x = 0; x < width; ++x) {
|
||||
uint8_t b = STATIC_CAST(uint8_t, src_rgb565[0] & 0x1f);
|
||||
uint8_t g = STATIC_CAST(
|
||||
uint8_t, (src_rgb565[0] >> 5) | ((src_rgb565[1] & 0x07) << 3));
|
||||
uint8_t r = STATIC_CAST(uint8_t, src_rgb565[1] >> 3);
|
||||
dst_argb[0] = STATIC_CAST(uint8_t, (b << 3) | (b >> 2));
|
||||
dst_argb[1] = STATIC_CAST(uint8_t, (g << 2) | (g >> 4));
|
||||
dst_argb[2] = STATIC_CAST(uint8_t, (r << 3) | (r >> 2));
|
||||
dst_argb[3] = 255u;
|
||||
dst_argb += 4;
|
||||
src_rgb565 += 2;
|
||||
}
|
||||
}
|
||||
|
||||
// 8 bit
|
||||
// Intel SSE/AVX uses the following equivalent formula
|
||||
// 0x7e80 = (66 + 129 + 25) * -128 + 0x1000 (for +16) and 0x0080 for round.
|
||||
// return (66 * ((int)r - 128) + 129 * ((int)g - 128) + 25 * ((int)b - 128) +
|
||||
// 0x7e80) >> 8;
|
||||
|
||||
static __inline uint8_t RGBToY(uint8_t r, uint8_t g, uint8_t b) {
|
||||
return STATIC_CAST(uint8_t, (66 * r + 129 * g + 25 * b + 0x1080) >> 8);
|
||||
}
|
||||
|
||||
#define AVGB(a, b) (((a) + (b) + 1) >> 1)
|
||||
|
||||
// LIBYUV_RGBTOU_TRUNCATE mimics x86 code that does not round.
|
||||
#ifdef LIBYUV_RGBTOU_TRUNCATE
|
||||
|
||||
static __inline uint8_t RGBToU(uint8_t r, uint8_t g, uint8_t b) {
|
||||
return STATIC_CAST(uint8_t, (112 * b - 74 * g - 38 * r + 0x8000) >> 8);
|
||||
}
|
||||
|
||||
static __inline uint8_t RGBToV(uint8_t r, uint8_t g, uint8_t b) {
|
||||
return STATIC_CAST(uint8_t, (112 * r - 94 * g - 18 * b + 0x8000) >> 8);
|
||||
}
|
||||
|
||||
#else
|
||||
// TODO(fbarchard): Add rounding to x86 SIMD and use this
|
||||
static __inline uint8_t RGBToU(uint8_t r, uint8_t g, uint8_t b) {
|
||||
return STATIC_CAST(uint8_t, (112 * b - 74 * g - 38 * r + 0x8080) >> 8);
|
||||
}
|
||||
static __inline uint8_t RGBToV(uint8_t r, uint8_t g, uint8_t b) {
|
||||
return STATIC_CAST(uint8_t, (112 * r - 94 * g - 18 * b + 0x8080) >> 8);
|
||||
}
|
||||
#endif
|
||||
|
||||
// LIBYUV_ARGBTOUV_PAVGB mimics x86 code that subsamples with 2 pavgb.
|
||||
#if !defined(LIBYUV_ARGBTOUV_PAVGB)
|
||||
static __inline int RGB2xToU(uint16_t r, uint16_t g, uint16_t b) {
|
||||
return STATIC_CAST(
|
||||
uint8_t, ((112 / 2) * b - (74 / 2) * g - (38 / 2) * r + 0x8080) >> 8);
|
||||
}
|
||||
static __inline int RGB2xToV(uint16_t r, uint16_t g, uint16_t b) {
|
||||
return STATIC_CAST(
|
||||
uint8_t, ((112 / 2) * r - (94 / 2) * g - (18 / 2) * b + 0x8080) >> 8);
|
||||
}
|
||||
#endif
|
||||
|
||||
// ARGBToY_C and ARGBToUV_C
|
||||
// Intel version mimic SSE/AVX which does 2 pavgb
|
||||
#if LIBYUV_ARGBTOUV_PAVGB
|
||||
#define MAKEROWY(NAME, R, G, B, BPP) \
|
||||
void NAME##ToYRow_C(const uint8_t* src_rgb, uint8_t* dst_y, int width) { \
|
||||
int x; \
|
||||
for (x = 0; x < width; ++x) { \
|
||||
dst_y[0] = RGBToY(src_rgb[R], src_rgb[G], src_rgb[B]); \
|
||||
src_rgb += BPP; \
|
||||
dst_y += 1; \
|
||||
} \
|
||||
} \
|
||||
void NAME##ToUVRow_C(const uint8_t* src_rgb, int src_stride_rgb, \
|
||||
uint8_t* dst_u, uint8_t* dst_v, int width) { \
|
||||
const uint8_t* src_rgb1 = src_rgb + src_stride_rgb; \
|
||||
int x; \
|
||||
for (x = 0; x < width - 1; x += 2) { \
|
||||
uint8_t ab = AVGB(AVGB(src_rgb[B], src_rgb1[B]), \
|
||||
AVGB(src_rgb[B + BPP], src_rgb1[B + BPP])); \
|
||||
uint8_t ag = AVGB(AVGB(src_rgb[G], src_rgb1[G]), \
|
||||
AVGB(src_rgb[G + BPP], src_rgb1[G + BPP])); \
|
||||
uint8_t ar = AVGB(AVGB(src_rgb[R], src_rgb1[R]), \
|
||||
AVGB(src_rgb[R + BPP], src_rgb1[R + BPP])); \
|
||||
dst_u[0] = RGBToU(ar, ag, ab); \
|
||||
dst_v[0] = RGBToV(ar, ag, ab); \
|
||||
src_rgb += BPP * 2; \
|
||||
src_rgb1 += BPP * 2; \
|
||||
dst_u += 1; \
|
||||
dst_v += 1; \
|
||||
} \
|
||||
if (width & 1) { \
|
||||
uint8_t ab = AVGB(src_rgb[B], src_rgb1[B]); \
|
||||
uint8_t ag = AVGB(src_rgb[G], src_rgb1[G]); \
|
||||
uint8_t ar = AVGB(src_rgb[R], src_rgb1[R]); \
|
||||
dst_u[0] = RGBToU(ar, ag, ab); \
|
||||
dst_v[0] = RGBToV(ar, ag, ab); \
|
||||
} \
|
||||
}
|
||||
#else
|
||||
// ARM version does sum / 2 then multiply by 2x smaller coefficients
|
||||
#define MAKEROWY(NAME, R, G, B, BPP) \
|
||||
void NAME##ToYRow_C(const uint8_t* src_rgb, uint8_t* dst_y, int width) { \
|
||||
int x; \
|
||||
for (x = 0; x < width; ++x) { \
|
||||
dst_y[0] = RGBToY(src_rgb[R], src_rgb[G], src_rgb[B]); \
|
||||
src_rgb += BPP; \
|
||||
dst_y += 1; \
|
||||
} \
|
||||
} \
|
||||
void NAME##ToUVRow_C(const uint8_t* src_rgb, int src_stride_rgb, \
|
||||
uint8_t* dst_u, uint8_t* dst_v, int width) { \
|
||||
const uint8_t* src_rgb1 = src_rgb + src_stride_rgb; \
|
||||
int x; \
|
||||
for (x = 0; x < width - 1; x += 2) { \
|
||||
uint16_t ab = (src_rgb[B] + src_rgb[B + BPP] + src_rgb1[B] + \
|
||||
src_rgb1[B + BPP] + 1) >> \
|
||||
1; \
|
||||
uint16_t ag = (src_rgb[G] + src_rgb[G + BPP] + src_rgb1[G] + \
|
||||
src_rgb1[G + BPP] + 1) >> \
|
||||
1; \
|
||||
uint16_t ar = (src_rgb[R] + src_rgb[R + BPP] + src_rgb1[R] + \
|
||||
src_rgb1[R + BPP] + 1) >> \
|
||||
1; \
|
||||
dst_u[0] = RGB2xToU(ar, ag, ab); \
|
||||
dst_v[0] = RGB2xToV(ar, ag, ab); \
|
||||
src_rgb += BPP * 2; \
|
||||
src_rgb1 += BPP * 2; \
|
||||
dst_u += 1; \
|
||||
dst_v += 1; \
|
||||
} \
|
||||
if (width & 1) { \
|
||||
uint16_t ab = src_rgb[B] + src_rgb1[B]; \
|
||||
uint16_t ag = src_rgb[G] + src_rgb1[G]; \
|
||||
uint16_t ar = src_rgb[R] + src_rgb1[R]; \
|
||||
dst_u[0] = RGB2xToU(ar, ag, ab); \
|
||||
dst_v[0] = RGB2xToV(ar, ag, ab); \
|
||||
} \
|
||||
}
|
||||
#endif
|
||||
|
||||
MAKEROWY(ARGB, 2, 1, 0, 4)
|
||||
|
||||
MAKEROWY(BGRA, 1, 2, 3, 4)
|
||||
|
||||
MAKEROWY(ABGR, 0, 1, 2, 4)
|
||||
|
||||
MAKEROWY(RGBA, 3, 2, 1, 4)
|
||||
|
||||
#undef MAKEROWY
|
||||
|
||||
// JPeg uses a variation on BT.601-1 full range
|
||||
// y = 0.29900 * r + 0.58700 * g + 0.11400 * b
|
||||
// u = -0.16874 * r - 0.33126 * g + 0.50000 * b + center
|
||||
// v = 0.50000 * r - 0.41869 * g - 0.08131 * b + center
|
||||
// BT.601 Mpeg range uses:
|
||||
// b 0.1016 * 255 = 25.908 = 25
|
||||
// g 0.5078 * 255 = 129.489 = 129
|
||||
// r 0.2578 * 255 = 65.739 = 66
|
||||
// JPeg 7 bit Y (deprecated)
|
||||
// b 0.11400 * 128 = 14.592 = 15
|
||||
// g 0.58700 * 128 = 75.136 = 75
|
||||
// r 0.29900 * 128 = 38.272 = 38
|
||||
// JPeg 8 bit Y:
|
||||
// b 0.11400 * 256 = 29.184 = 29
|
||||
// g 0.58700 * 256 = 150.272 = 150
|
||||
// r 0.29900 * 256 = 76.544 = 77
|
||||
// JPeg 8 bit U:
|
||||
// b 0.50000 * 255 = 127.5 = 127
|
||||
// g -0.33126 * 255 = -84.4713 = -84
|
||||
// r -0.16874 * 255 = -43.0287 = -43
|
||||
// JPeg 8 bit V:
|
||||
// b -0.08131 * 255 = -20.73405 = -20
|
||||
// g -0.41869 * 255 = -106.76595 = -107
|
||||
// r 0.50000 * 255 = 127.5 = 127
|
||||
|
||||
// 8 bit
|
||||
static __inline uint8_t RGBToYJ(uint8_t r, uint8_t g, uint8_t b) {
|
||||
return (77 * r + 150 * g + 29 * b + 128) >> 8;
|
||||
}
|
||||
|
||||
#if defined(LIBYUV_ARGBTOUV_PAVGB)
|
||||
|
||||
static __inline uint8_t RGBToUJ(uint8_t r, uint8_t g, uint8_t b) {
|
||||
return (127 * b - 84 * g - 43 * r + 0x8080) >> 8;
|
||||
}
|
||||
|
||||
static __inline uint8_t RGBToVJ(uint8_t r, uint8_t g, uint8_t b) {
|
||||
return (127 * r - 107 * g - 20 * b + 0x8080) >> 8;
|
||||
}
|
||||
|
||||
#else
|
||||
static __inline uint8_t RGB2xToUJ(uint16_t r, uint16_t g, uint16_t b) {
|
||||
return ((127 / 2) * b - (84 / 2) * g - (43 / 2) * r + 0x8080) >> 8;
|
||||
}
|
||||
static __inline uint8_t RGB2xToVJ(uint16_t r, uint16_t g, uint16_t b) {
|
||||
return ((127 / 2) * r - (107 / 2) * g - (20 / 2) * b + 0x8080) >> 8;
|
||||
}
|
||||
#endif
|
||||
|
||||
// ARGBToYJ_C and ARGBToUVJ_C
|
||||
// Intel version mimic SSE/AVX which does 2 pavgb
|
||||
#if LIBYUV_ARGBTOUV_PAVGB
|
||||
#define MAKEROWYJ(NAME, R, G, B, BPP) \
|
||||
void NAME##ToYJRow_C(const uint8_t* src_rgb, uint8_t* dst_y, int width) { \
|
||||
int x; \
|
||||
for (x = 0; x < width; ++x) { \
|
||||
dst_y[0] = RGBToYJ(src_rgb[R], src_rgb[G], src_rgb[B]); \
|
||||
src_rgb += BPP; \
|
||||
dst_y += 1; \
|
||||
} \
|
||||
} \
|
||||
void NAME##ToUVJRow_C(const uint8_t* src_rgb, int src_stride_rgb, \
|
||||
uint8_t* dst_u, uint8_t* dst_v, int width) { \
|
||||
const uint8_t* src_rgb1 = src_rgb + src_stride_rgb; \
|
||||
int x; \
|
||||
for (x = 0; x < width - 1; x += 2) { \
|
||||
uint8_t ab = AVGB(AVGB(src_rgb[B], src_rgb1[B]), \
|
||||
AVGB(src_rgb[B + BPP], src_rgb1[B + BPP])); \
|
||||
uint8_t ag = AVGB(AVGB(src_rgb[G], src_rgb1[G]), \
|
||||
AVGB(src_rgb[G + BPP], src_rgb1[G + BPP])); \
|
||||
uint8_t ar = AVGB(AVGB(src_rgb[R], src_rgb1[R]), \
|
||||
AVGB(src_rgb[R + BPP], src_rgb1[R + BPP])); \
|
||||
dst_u[0] = RGBToUJ(ar, ag, ab); \
|
||||
dst_v[0] = RGBToVJ(ar, ag, ab); \
|
||||
src_rgb += BPP * 2; \
|
||||
src_rgb1 += BPP * 2; \
|
||||
dst_u += 1; \
|
||||
dst_v += 1; \
|
||||
} \
|
||||
if (width & 1) { \
|
||||
uint8_t ab = AVGB(src_rgb[B], src_rgb1[B]); \
|
||||
uint8_t ag = AVGB(src_rgb[G], src_rgb1[G]); \
|
||||
uint8_t ar = AVGB(src_rgb[R], src_rgb1[R]); \
|
||||
dst_u[0] = RGBToUJ(ar, ag, ab); \
|
||||
dst_v[0] = RGBToVJ(ar, ag, ab); \
|
||||
} \
|
||||
}
|
||||
#else
|
||||
// ARM version does sum / 2 then multiply by 2x smaller coefficients
|
||||
#define MAKEROWYJ(NAME, R, G, B, BPP) \
|
||||
void NAME##ToYJRow_C(const uint8_t* src_rgb, uint8_t* dst_y, int width) { \
|
||||
int x; \
|
||||
for (x = 0; x < width; ++x) { \
|
||||
dst_y[0] = RGBToYJ(src_rgb[R], src_rgb[G], src_rgb[B]); \
|
||||
src_rgb += BPP; \
|
||||
dst_y += 1; \
|
||||
} \
|
||||
} \
|
||||
void NAME##ToUVJRow_C(const uint8_t* src_rgb, int src_stride_rgb, \
|
||||
uint8_t* dst_u, uint8_t* dst_v, int width) { \
|
||||
const uint8_t* src_rgb1 = src_rgb + src_stride_rgb; \
|
||||
int x; \
|
||||
for (x = 0; x < width - 1; x += 2) { \
|
||||
uint16_t ab = (src_rgb[B] + src_rgb[B + BPP] + src_rgb1[B] + \
|
||||
src_rgb1[B + BPP] + 1) >> \
|
||||
1; \
|
||||
uint16_t ag = (src_rgb[G] + src_rgb[G + BPP] + src_rgb1[G] + \
|
||||
src_rgb1[G + BPP] + 1) >> \
|
||||
1; \
|
||||
uint16_t ar = (src_rgb[R] + src_rgb[R + BPP] + src_rgb1[R] + \
|
||||
src_rgb1[R + BPP] + 1) >> \
|
||||
1; \
|
||||
dst_u[0] = RGB2xToUJ(ar, ag, ab); \
|
||||
dst_v[0] = RGB2xToVJ(ar, ag, ab); \
|
||||
src_rgb += BPP * 2; \
|
||||
src_rgb1 += BPP * 2; \
|
||||
dst_u += 1; \
|
||||
dst_v += 1; \
|
||||
} \
|
||||
if (width & 1) { \
|
||||
uint16_t ab = (src_rgb[B] + src_rgb1[B]); \
|
||||
uint16_t ag = (src_rgb[G] + src_rgb1[G]); \
|
||||
uint16_t ar = (src_rgb[R] + src_rgb1[R]); \
|
||||
dst_u[0] = RGB2xToUJ(ar, ag, ab); \
|
||||
dst_v[0] = RGB2xToVJ(ar, ag, ab); \
|
||||
} \
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
MAKEROWYJ(ARGB, 2, 1, 0, 4)
|
||||
|
||||
MAKEROWYJ(ABGR, 0, 1, 2, 4)
|
||||
|
||||
MAKEROWYJ(RGBA, 3, 2, 1, 4)
|
||||
|
||||
MAKEROWYJ(RGB24, 2, 1, 0, 3)
|
||||
|
||||
MAKEROWYJ(RAW, 0, 1, 2, 3)
|
||||
|
||||
#undef MAKEROWYJ
|
||||
|
||||
void RGB565ToYRow_C(const uint8_t *src_rgb565, uint8_t *dst_y, int width) {
|
||||
int x;
|
||||
for (x = 0; x < width; ++x) {
|
||||
uint8_t b = src_rgb565[0] & 0x1f;
|
||||
uint8_t g = STATIC_CAST(
|
||||
uint8_t, (src_rgb565[0] >> 5) | ((src_rgb565[1] & 0x07) << 3));
|
||||
uint8_t r = src_rgb565[1] >> 3;
|
||||
b = STATIC_CAST(uint8_t, (b << 3) | (b >> 2));
|
||||
g = STATIC_CAST(uint8_t, (g << 2) | (g >> 4));
|
||||
r = STATIC_CAST(uint8_t, (r << 3) | (r >> 2));
|
||||
dst_y[0] = RGBToY(r, g, b);
|
||||
src_rgb565 += 2;
|
||||
dst_y += 1;
|
||||
}
|
||||
}
|
||||
|
||||
void RGB565ToUVRow_C(const uint8_t *src_rgb565,
|
||||
int src_stride_rgb565,
|
||||
uint8_t *dst_u,
|
||||
uint8_t *dst_v,
|
||||
int width) {
|
||||
const uint8_t *next_rgb565 = src_rgb565 + src_stride_rgb565;
|
||||
int x;
|
||||
for (x = 0; x < width - 1; x += 2) {
|
||||
uint8_t b0 = STATIC_CAST(uint8_t, src_rgb565[0] & 0x1f);
|
||||
uint8_t g0 = STATIC_CAST(
|
||||
uint8_t, (src_rgb565[0] >> 5) | ((src_rgb565[1] & 0x07) << 3));
|
||||
uint8_t r0 = STATIC_CAST(uint8_t, src_rgb565[1] >> 3);
|
||||
uint8_t b1 = STATIC_CAST(uint8_t, src_rgb565[2] & 0x1f);
|
||||
uint8_t g1 = STATIC_CAST(
|
||||
uint8_t, (src_rgb565[2] >> 5) | ((src_rgb565[3] & 0x07) << 3));
|
||||
uint8_t r1 = STATIC_CAST(uint8_t, src_rgb565[3] >> 3);
|
||||
uint8_t b2 = STATIC_CAST(uint8_t, next_rgb565[0] & 0x1f);
|
||||
uint8_t g2 = STATIC_CAST(
|
||||
uint8_t, (next_rgb565[0] >> 5) | ((next_rgb565[1] & 0x07) << 3));
|
||||
uint8_t r2 = STATIC_CAST(uint8_t, next_rgb565[1] >> 3);
|
||||
uint8_t b3 = STATIC_CAST(uint8_t, next_rgb565[2] & 0x1f);
|
||||
uint8_t g3 = STATIC_CAST(
|
||||
uint8_t, (next_rgb565[2] >> 5) | ((next_rgb565[3] & 0x07) << 3));
|
||||
uint8_t r3 = STATIC_CAST(uint8_t, next_rgb565[3] >> 3);
|
||||
|
||||
b0 = STATIC_CAST(uint8_t, (b0 << 3) | (b0 >> 2));
|
||||
g0 = STATIC_CAST(uint8_t, (g0 << 2) | (g0 >> 4));
|
||||
r0 = STATIC_CAST(uint8_t, (r0 << 3) | (r0 >> 2));
|
||||
b1 = STATIC_CAST(uint8_t, (b1 << 3) | (b1 >> 2));
|
||||
g1 = STATIC_CAST(uint8_t, (g1 << 2) | (g1 >> 4));
|
||||
r1 = STATIC_CAST(uint8_t, (r1 << 3) | (r1 >> 2));
|
||||
b2 = STATIC_CAST(uint8_t, (b2 << 3) | (b2 >> 2));
|
||||
g2 = STATIC_CAST(uint8_t, (g2 << 2) | (g2 >> 4));
|
||||
r2 = STATIC_CAST(uint8_t, (r2 << 3) | (r2 >> 2));
|
||||
b3 = STATIC_CAST(uint8_t, (b3 << 3) | (b3 >> 2));
|
||||
g3 = STATIC_CAST(uint8_t, (g3 << 2) | (g3 >> 4));
|
||||
r3 = STATIC_CAST(uint8_t, (r3 << 3) | (r3 >> 2));
|
||||
|
||||
#if LIBYUV_ARGBTOUV_PAVGB
|
||||
uint8_t ab = AVGB(AVGB(b0, b2), AVGB(b1, b3));
|
||||
uint8_t ag = AVGB(AVGB(g0, g2), AVGB(g1, g3));
|
||||
uint8_t ar = AVGB(AVGB(r0, r2), AVGB(r1, r3));
|
||||
dst_u[0] = RGBToU(ar, ag, ab);
|
||||
dst_v[0] = RGBToV(ar, ag, ab);
|
||||
#else
|
||||
uint16_t b = (b0 + b1 + b2 + b3 + 1) >> 1;
|
||||
uint16_t g = (g0 + g1 + g2 + g3 + 1) >> 1;
|
||||
uint16_t r = (r0 + r1 + r2 + r3 + 1) >> 1;
|
||||
dst_u[0] = RGB2xToU(r, g, b);
|
||||
dst_v[0] = RGB2xToV(r, g, b);
|
||||
#endif
|
||||
|
||||
src_rgb565 += 4;
|
||||
next_rgb565 += 4;
|
||||
dst_u += 1;
|
||||
dst_v += 1;
|
||||
}
|
||||
if (width & 1) {
|
||||
uint8_t b0 = STATIC_CAST(uint8_t, src_rgb565[0] & 0x1f);
|
||||
uint8_t g0 = STATIC_CAST(
|
||||
uint8_t, (src_rgb565[0] >> 5) | ((src_rgb565[1] & 0x07) << 3));
|
||||
uint8_t r0 = STATIC_CAST(uint8_t, src_rgb565[1] >> 3);
|
||||
uint8_t b2 = STATIC_CAST(uint8_t, next_rgb565[0] & 0x1f);
|
||||
uint8_t g2 = STATIC_CAST(
|
||||
uint8_t, (next_rgb565[0] >> 5) | ((next_rgb565[1] & 0x07) << 3));
|
||||
uint8_t r2 = STATIC_CAST(uint8_t, next_rgb565[1] >> 3);
|
||||
b0 = STATIC_CAST(uint8_t, (b0 << 3) | (b0 >> 2));
|
||||
g0 = STATIC_CAST(uint8_t, (g0 << 2) | (g0 >> 4));
|
||||
r0 = STATIC_CAST(uint8_t, (r0 << 3) | (r0 >> 2));
|
||||
b2 = STATIC_CAST(uint8_t, (b2 << 3) | (b2 >> 2));
|
||||
g2 = STATIC_CAST(uint8_t, (g2 << 2) | (g2 >> 4));
|
||||
r2 = STATIC_CAST(uint8_t, (r2 << 3) | (r2 >> 2));
|
||||
|
||||
#if LIBYUV_ARGBTOUV_PAVGB
|
||||
uint8_t ab = AVGB(b0, b2);
|
||||
uint8_t ag = AVGB(g0, g2);
|
||||
uint8_t ar = AVGB(r0, r2);
|
||||
dst_u[0] = RGBToU(ar, ag, ab);
|
||||
dst_v[0] = RGBToV(ar, ag, ab);
|
||||
#else
|
||||
uint16_t b = b0 + b2;
|
||||
uint16_t g = g0 + g2;
|
||||
uint16_t r = r0 + r2;
|
||||
dst_u[0] = RGB2xToU(r, g, b);
|
||||
dst_v[0] = RGB2xToV(r, g, b);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#define REPEAT8(v) (v) | ((v) << 8)
|
||||
#define SHADE(f, v) v* f >> 24
|
||||
|
||||
#undef REPEAT8
|
||||
#undef SHADE
|
||||
|
||||
#define REPEAT8(v) (v) | ((v) << 8)
|
||||
#define SHADE(f, v) v* f >> 16
|
||||
|
||||
#undef REPEAT8
|
||||
#undef SHADE
|
||||
|
||||
#define SHADE(f, v) clamp255(v + f)
|
||||
|
||||
#undef SHADE
|
||||
|
||||
#define SHADE(f, v) clamp0(f - v)
|
||||
|
||||
#undef SHADE
|
||||
|
||||
// Macros to create SIMD specific yuv to rgb conversion constants.
|
||||
|
||||
// clang-format off
|
||||
|
||||
#if defined(__aarch64__) || defined(__arm__)
|
||||
// Bias values include subtract 128 from U and V, bias from Y and rounding.
|
||||
// For B and R bias is negative. For G bias is positive.
|
||||
#define YUVCONSTANTSBODY(YG, YB, UB, UG, VG, VR) \
|
||||
{{UB, VR, UG, VG, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, \
|
||||
{YG, (UB * 128 - YB), (UG * 128 + VG * 128 + YB), (VR * 128 - YB), YB, 0, \
|
||||
0, 0}}
|
||||
#else
|
||||
#define YUVCONSTANTSBODY(YG, YB, UB, UG, VG, VR) \
|
||||
{{UB, 0, UB, 0, UB, 0, UB, 0, UB, 0, UB, 0, UB, 0, UB, 0, \
|
||||
UB, 0, UB, 0, UB, 0, UB, 0, UB, 0, UB, 0, UB, 0, UB, 0}, \
|
||||
{UG, VG, UG, VG, UG, VG, UG, VG, UG, VG, UG, VG, UG, VG, UG, VG, \
|
||||
UG, VG, UG, VG, UG, VG, UG, VG, UG, VG, UG, VG, UG, VG, UG, VG}, \
|
||||
{0, VR, 0, VR, 0, VR, 0, VR, 0, VR, 0, VR, 0, VR, 0, VR, \
|
||||
0, VR, 0, VR, 0, VR, 0, VR, 0, VR, 0, VR, 0, VR, 0, VR}, \
|
||||
{YG, YG, YG, YG, YG, YG, YG, YG, YG, YG, YG, YG, YG, YG, YG, YG}, \
|
||||
{YB, YB, YB, YB, YB, YB, YB, YB, YB, YB, YB, YB, YB, YB, YB, YB}}
|
||||
#endif
|
||||
|
||||
// clang-format on
|
||||
|
||||
#define MAKEYUVCONSTANTS(name, YG, YB, UB, UG, VG, VR) \
|
||||
const struct YuvConstants SIMD_ALIGNED(kYuv##name##Constants) = \
|
||||
YUVCONSTANTSBODY(YG, YB, UB, UG, VG, VR); \
|
||||
const struct YuvConstants SIMD_ALIGNED(kYvu##name##Constants) = \
|
||||
YUVCONSTANTSBODY(YG, YB, VR, VG, UG, UB);
|
||||
|
||||
// TODO(fbarchard): Generate SIMD structures from float matrix.
|
||||
|
||||
// BT.601 limited range YUV to RGB reference
|
||||
// R = (Y - 16) * 1.164 + V * 1.596
|
||||
// G = (Y - 16) * 1.164 - U * 0.391 - V * 0.813
|
||||
// B = (Y - 16) * 1.164 + U * 2.018
|
||||
// KR = 0.299; KB = 0.114
|
||||
|
||||
// U and V contributions to R,G,B.
|
||||
#if defined(LIBYUV_UNLIMITED_DATA) || defined(LIBYUV_UNLIMITED_BT601)
|
||||
#define UB 129 /* round(2.018 * 64) */
|
||||
#else
|
||||
#define UB 128 /* max(128, round(2.018 * 64)) */
|
||||
#endif
|
||||
#define UG 25 /* round(0.391 * 64) */
|
||||
#define VG 52 /* round(0.813 * 64) */
|
||||
#define VR 102 /* round(1.596 * 64) */
|
||||
|
||||
// Y contribution to R,G,B. Scale and bias.
|
||||
#define YG 18997 /* round(1.164 * 64 * 256 * 256 / 257) */
|
||||
#define YB -1160 /* 1.164 * 64 * -16 + 64 / 2 */
|
||||
|
||||
MAKEYUVCONSTANTS(I601, YG, YB, UB, UG, VG, VR)
|
||||
|
||||
#undef YG
|
||||
#undef YB
|
||||
#undef UB
|
||||
#undef UG
|
||||
#undef VG
|
||||
#undef VR
|
||||
|
||||
// BT.601 full range YUV to RGB reference (aka JPEG)
|
||||
// * R = Y + V * 1.40200
|
||||
// * G = Y - U * 0.34414 - V * 0.71414
|
||||
// * B = Y + U * 1.77200
|
||||
// KR = 0.299; KB = 0.114
|
||||
|
||||
// U and V contributions to R,G,B.
|
||||
#define UB 113 /* round(1.77200 * 64) */
|
||||
#define UG 22 /* round(0.34414 * 64) */
|
||||
#define VG 46 /* round(0.71414 * 64) */
|
||||
#define VR 90 /* round(1.40200 * 64) */
|
||||
|
||||
// Y contribution to R,G,B. Scale and bias.
|
||||
#define YG 16320 /* round(1.000 * 64 * 256 * 256 / 257) */
|
||||
#define YB 32 /* 64 / 2 */
|
||||
|
||||
MAKEYUVCONSTANTS(JPEG, YG, YB, UB, UG, VG, VR)
|
||||
|
||||
#undef YG
|
||||
#undef YB
|
||||
#undef UB
|
||||
#undef UG
|
||||
#undef VG
|
||||
#undef VR
|
||||
|
||||
// BT.709 limited range YUV to RGB reference
|
||||
// R = (Y - 16) * 1.164 + V * 1.793
|
||||
// G = (Y - 16) * 1.164 - U * 0.213 - V * 0.533
|
||||
// B = (Y - 16) * 1.164 + U * 2.112
|
||||
// KR = 0.2126, KB = 0.0722
|
||||
|
||||
// U and V contributions to R,G,B.
|
||||
#if defined(LIBYUV_UNLIMITED_DATA) || defined(LIBYUV_UNLIMITED_BT709)
|
||||
#define UB 135 /* round(2.112 * 64) */
|
||||
#else
|
||||
#define UB 128 /* max(128, round(2.112 * 64)) */
|
||||
#endif
|
||||
#define UG 14 /* round(0.213 * 64) */
|
||||
#define VG 34 /* round(0.533 * 64) */
|
||||
#define VR 115 /* round(1.793 * 64) */
|
||||
|
||||
// Y contribution to R,G,B. Scale and bias.
|
||||
#define YG 18997 /* round(1.164 * 64 * 256 * 256 / 257) */
|
||||
#define YB -1160 /* 1.164 * 64 * -16 + 64 / 2 */
|
||||
|
||||
MAKEYUVCONSTANTS(H709, YG, YB, UB, UG, VG, VR)
|
||||
|
||||
#undef YG
|
||||
#undef YB
|
||||
#undef UB
|
||||
#undef UG
|
||||
#undef VG
|
||||
#undef VR
|
||||
|
||||
// BT.709 full range YUV to RGB reference
|
||||
// R = Y + V * 1.5748
|
||||
// G = Y - U * 0.18732 - V * 0.46812
|
||||
// B = Y + U * 1.8556
|
||||
// KR = 0.2126, KB = 0.0722
|
||||
|
||||
// U and V contributions to R,G,B.
|
||||
#define UB 119 /* round(1.8556 * 64) */
|
||||
#define UG 12 /* round(0.18732 * 64) */
|
||||
#define VG 30 /* round(0.46812 * 64) */
|
||||
#define VR 101 /* round(1.5748 * 64) */
|
||||
|
||||
// Y contribution to R,G,B. Scale and bias. (same as jpeg)
|
||||
#define YG 16320 /* round(1 * 64 * 256 * 256 / 257) */
|
||||
#define YB 32 /* 64 / 2 */
|
||||
|
||||
MAKEYUVCONSTANTS(F709, YG, YB, UB, UG, VG, VR)
|
||||
|
||||
#undef YG
|
||||
#undef YB
|
||||
#undef UB
|
||||
#undef UG
|
||||
#undef VG
|
||||
#undef VR
|
||||
|
||||
// BT.2020 limited range YUV to RGB reference
|
||||
// R = (Y - 16) * 1.164384 + V * 1.67867
|
||||
// G = (Y - 16) * 1.164384 - U * 0.187326 - V * 0.65042
|
||||
// B = (Y - 16) * 1.164384 + U * 2.14177
|
||||
// KR = 0.2627; KB = 0.0593
|
||||
|
||||
// U and V contributions to R,G,B.
|
||||
#if defined(LIBYUV_UNLIMITED_DATA) || defined(LIBYUV_UNLIMITED_BT2020)
|
||||
#define UB 137 /* round(2.142 * 64) */
|
||||
#else
|
||||
#define UB 128 /* max(128, round(2.142 * 64)) */
|
||||
#endif
|
||||
#define UG 12 /* round(0.187326 * 64) */
|
||||
#define VG 42 /* round(0.65042 * 64) */
|
||||
#define VR 107 /* round(1.67867 * 64) */
|
||||
|
||||
// Y contribution to R,G,B. Scale and bias.
|
||||
#define YG 19003 /* round(1.164384 * 64 * 256 * 256 / 257) */
|
||||
#define YB -1160 /* 1.164384 * 64 * -16 + 64 / 2 */
|
||||
|
||||
MAKEYUVCONSTANTS(2020, YG, YB, UB, UG, VG, VR)
|
||||
|
||||
#undef YG
|
||||
#undef YB
|
||||
#undef UB
|
||||
#undef UG
|
||||
#undef VG
|
||||
#undef VR
|
||||
|
||||
// BT.2020 full range YUV to RGB reference
|
||||
// R = Y + V * 1.474600
|
||||
// G = Y - U * 0.164553 - V * 0.571353
|
||||
// B = Y + U * 1.881400
|
||||
// KR = 0.2627; KB = 0.0593
|
||||
|
||||
#define UB 120 /* round(1.881400 * 64) */
|
||||
#define UG 11 /* round(0.164553 * 64) */
|
||||
#define VG 37 /* round(0.571353 * 64) */
|
||||
#define VR 94 /* round(1.474600 * 64) */
|
||||
|
||||
// Y contribution to R,G,B. Scale and bias. (same as jpeg)
|
||||
#define YG 16320 /* round(1 * 64 * 256 * 256 / 257) */
|
||||
#define YB 32 /* 64 / 2 */
|
||||
|
||||
MAKEYUVCONSTANTS(V2020, YG, YB, UB, UG, VG, VR)
|
||||
|
||||
#undef YG
|
||||
#undef YB
|
||||
#undef UB
|
||||
#undef UG
|
||||
#undef VG
|
||||
#undef VR
|
||||
|
||||
#undef BB
|
||||
#undef BG
|
||||
#undef BR
|
||||
|
||||
#undef MAKEYUVCONSTANTS
|
||||
|
||||
#if defined(__aarch64__) || defined(__arm__)
|
||||
#define LOAD_YUV_CONSTANTS \
|
||||
int ub = yuvconstants->kUVCoeff[0]; \
|
||||
int vr = yuvconstants->kUVCoeff[1]; \
|
||||
int ug = yuvconstants->kUVCoeff[2]; \
|
||||
int vg = yuvconstants->kUVCoeff[3]; \
|
||||
int yg = yuvconstants->kRGBCoeffBias[0]; \
|
||||
int bb = yuvconstants->kRGBCoeffBias[1]; \
|
||||
int bg = yuvconstants->kRGBCoeffBias[2]; \
|
||||
int br = yuvconstants->kRGBCoeffBias[3]
|
||||
|
||||
#define CALC_RGB16 \
|
||||
int32_t y1 = (uint32_t)(y32 * yg) >> 16; \
|
||||
int b16 = y1 + (u * ub) - bb; \
|
||||
int g16 = y1 + bg - (u * ug + v * vg); \
|
||||
int r16 = y1 + (v * vr) - br
|
||||
#else
|
||||
#define LOAD_YUV_CONSTANTS \
|
||||
int ub = yuvconstants->kUVToB[0]; \
|
||||
int ug = yuvconstants->kUVToG[0]; \
|
||||
int vg = yuvconstants->kUVToG[1]; \
|
||||
int vr = yuvconstants->kUVToR[1]; \
|
||||
int yg = yuvconstants->kYToRgb[0]; \
|
||||
int yb = yuvconstants->kYBiasToRgb[0]
|
||||
|
||||
#define CALC_RGB16 \
|
||||
int32_t y1 = ((uint32_t)(y32 * yg) >> 16) + yb; \
|
||||
int8_t ui = (int8_t)u; \
|
||||
int8_t vi = (int8_t)v; \
|
||||
ui -= 0x80; \
|
||||
vi -= 0x80; \
|
||||
int b16 = y1 + (ui * ub); \
|
||||
int g16 = y1 - (ui * ug + vi * vg); \
|
||||
int r16 = y1 + (vi * vr)
|
||||
#endif
|
||||
|
||||
void MirrorRow_C(const uint8_t *src, uint8_t *dst, int width) {
|
||||
int x;
|
||||
src += width - 1;
|
||||
for (x = 0; x < width - 1; x += 2) {
|
||||
dst[x] = src[0];
|
||||
dst[x + 1] = src[-1];
|
||||
src -= 2;
|
||||
}
|
||||
if (width & 1) {
|
||||
dst[width - 1] = src[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Use scale to convert lsb formats to msb, depending how many bits there are:
|
||||
// 32768 = 9 bits
|
||||
// 16384 = 10 bits
|
||||
// 4096 = 12 bits
|
||||
// 256 = 16 bits
|
||||
// TODO(fbarchard): change scale to bits
|
||||
#define C16TO8(v, scale) clamp255(((v) * (scale)) >> 16)
|
||||
|
||||
void CopyRow_C(const uint8_t *src, uint8_t *dst, int count) {
|
||||
memcpy(dst, src, count);
|
||||
}
|
||||
|
||||
// Divide source RGB by alpha and store to destination.
|
||||
// b = (b * 255 + (a / 2)) / a;
|
||||
// g = (g * 255 + (a / 2)) / a;
|
||||
// r = (r * 255 + (a / 2)) / a;
|
||||
// Reciprocal method is off by 1 on some values. ie 125
|
||||
// 8.8 fixed point inverse table with 1.0 in upper short and 1 / a in lower.
|
||||
#define T(a) 0x01000000 + (0x10000 / a)
|
||||
const uint32_t fixed_invtbl8[256] = {
|
||||
0x01000000, 0x0100ffff, T(0x02), T(0x03), T(0x04), T(0x05), T(0x06),
|
||||
T(0x07), T(0x08), T(0x09), T(0x0a), T(0x0b), T(0x0c), T(0x0d),
|
||||
T(0x0e), T(0x0f), T(0x10), T(0x11), T(0x12), T(0x13), T(0x14),
|
||||
T(0x15), T(0x16), T(0x17), T(0x18), T(0x19), T(0x1a), T(0x1b),
|
||||
T(0x1c), T(0x1d), T(0x1e), T(0x1f), T(0x20), T(0x21), T(0x22),
|
||||
T(0x23), T(0x24), T(0x25), T(0x26), T(0x27), T(0x28), T(0x29),
|
||||
T(0x2a), T(0x2b), T(0x2c), T(0x2d), T(0x2e), T(0x2f), T(0x30),
|
||||
T(0x31), T(0x32), T(0x33), T(0x34), T(0x35), T(0x36), T(0x37),
|
||||
T(0x38), T(0x39), T(0x3a), T(0x3b), T(0x3c), T(0x3d), T(0x3e),
|
||||
T(0x3f), T(0x40), T(0x41), T(0x42), T(0x43), T(0x44), T(0x45),
|
||||
T(0x46), T(0x47), T(0x48), T(0x49), T(0x4a), T(0x4b), T(0x4c),
|
||||
T(0x4d), T(0x4e), T(0x4f), T(0x50), T(0x51), T(0x52), T(0x53),
|
||||
T(0x54), T(0x55), T(0x56), T(0x57), T(0x58), T(0x59), T(0x5a),
|
||||
T(0x5b), T(0x5c), T(0x5d), T(0x5e), T(0x5f), T(0x60), T(0x61),
|
||||
T(0x62), T(0x63), T(0x64), T(0x65), T(0x66), T(0x67), T(0x68),
|
||||
T(0x69), T(0x6a), T(0x6b), T(0x6c), T(0x6d), T(0x6e), T(0x6f),
|
||||
T(0x70), T(0x71), T(0x72), T(0x73), T(0x74), T(0x75), T(0x76),
|
||||
T(0x77), T(0x78), T(0x79), T(0x7a), T(0x7b), T(0x7c), T(0x7d),
|
||||
T(0x7e), T(0x7f), T(0x80), T(0x81), T(0x82), T(0x83), T(0x84),
|
||||
T(0x85), T(0x86), T(0x87), T(0x88), T(0x89), T(0x8a), T(0x8b),
|
||||
T(0x8c), T(0x8d), T(0x8e), T(0x8f), T(0x90), T(0x91), T(0x92),
|
||||
T(0x93), T(0x94), T(0x95), T(0x96), T(0x97), T(0x98), T(0x99),
|
||||
T(0x9a), T(0x9b), T(0x9c), T(0x9d), T(0x9e), T(0x9f), T(0xa0),
|
||||
T(0xa1), T(0xa2), T(0xa3), T(0xa4), T(0xa5), T(0xa6), T(0xa7),
|
||||
T(0xa8), T(0xa9), T(0xaa), T(0xab), T(0xac), T(0xad), T(0xae),
|
||||
T(0xaf), T(0xb0), T(0xb1), T(0xb2), T(0xb3), T(0xb4), T(0xb5),
|
||||
T(0xb6), T(0xb7), T(0xb8), T(0xb9), T(0xba), T(0xbb), T(0xbc),
|
||||
T(0xbd), T(0xbe), T(0xbf), T(0xc0), T(0xc1), T(0xc2), T(0xc3),
|
||||
T(0xc4), T(0xc5), T(0xc6), T(0xc7), T(0xc8), T(0xc9), T(0xca),
|
||||
T(0xcb), T(0xcc), T(0xcd), T(0xce), T(0xcf), T(0xd0), T(0xd1),
|
||||
T(0xd2), T(0xd3), T(0xd4), T(0xd5), T(0xd6), T(0xd7), T(0xd8),
|
||||
T(0xd9), T(0xda), T(0xdb), T(0xdc), T(0xdd), T(0xde), T(0xdf),
|
||||
T(0xe0), T(0xe1), T(0xe2), T(0xe3), T(0xe4), T(0xe5), T(0xe6),
|
||||
T(0xe7), T(0xe8), T(0xe9), T(0xea), T(0xeb), T(0xec), T(0xed),
|
||||
T(0xee), T(0xef), T(0xf0), T(0xf1), T(0xf2), T(0xf3), T(0xf4),
|
||||
T(0xf5), T(0xf6), T(0xf7), T(0xf8), T(0xf9), T(0xfa), T(0xfb),
|
||||
T(0xfc), T(0xfd), T(0xfe), 0x01000100};
|
||||
#undef T
|
||||
|
||||
// Blend 2 rows into 1.
|
||||
static void HalfRow_C(const uint8_t *src_uv,
|
||||
ptrdiff_t src_uv_stride,
|
||||
uint8_t *dst_uv,
|
||||
int width) {
|
||||
int x;
|
||||
for (x = 0; x < width; ++x) {
|
||||
dst_uv[x] = (src_uv[x] + src_uv[src_uv_stride + x] + 1) >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
// C version 2x2 -> 2x1.
|
||||
void InterpolateRow_C(uint8_t *dst_ptr,
|
||||
const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
int width,
|
||||
int source_y_fraction) {
|
||||
int y1_fraction = source_y_fraction;
|
||||
int y0_fraction = 256 - y1_fraction;
|
||||
const uint8_t *src_ptr1 = src_ptr + src_stride;
|
||||
int x;
|
||||
assert(source_y_fraction >= 0);
|
||||
assert(source_y_fraction < 256);
|
||||
|
||||
if (y1_fraction == 0) {
|
||||
memcpy(dst_ptr, src_ptr, width);
|
||||
return;
|
||||
}
|
||||
if (y1_fraction == 128) {
|
||||
HalfRow_C(src_ptr, src_stride, dst_ptr, width);
|
||||
return;
|
||||
}
|
||||
for (x = 0; x < width; ++x) {
|
||||
dst_ptr[0] = STATIC_CAST(
|
||||
uint8_t,
|
||||
(src_ptr[0] * y0_fraction + src_ptr1[0] * y1_fraction + 128) >> 8);
|
||||
++src_ptr;
|
||||
++src_ptr1;
|
||||
++dst_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Work around GCC 7 punning warning -Wstrict-aliasing
|
||||
#if defined(__GNUC__)
|
||||
typedef uint32_t __attribute__((__may_alias__)) uint32_alias_t;
|
||||
#else
|
||||
typedef uint32_t uint32_alias_t;
|
||||
#endif
|
||||
|
||||
#undef STATIC_CAST
|
||||
1090
pkg/encoder/yuv/libyuv/row_gcc.c
Normal file
1090
pkg/encoder/yuv/libyuv/row_gcc.c
Normal file
File diff suppressed because it is too large
Load diff
946
pkg/encoder/yuv/libyuv/scale.c
Normal file
946
pkg/encoder/yuv/libyuv/scale.c
Normal file
|
|
@ -0,0 +1,946 @@
|
|||
/*
|
||||
* Copyright 2011 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "scale.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "cpu_id.h"
|
||||
#include "planar_functions.h" // For CopyPlane
|
||||
#include "row.h"
|
||||
#include "scale_row.h"
|
||||
|
||||
static __inline int Abs(int v) {
|
||||
return v >= 0 ? v : -v;
|
||||
}
|
||||
|
||||
#define SUBSAMPLE(v, a, s) (v < 0) ? (-((-v + a) >> s)) : ((v + a) >> s)
|
||||
#define CENTERSTART(dx, s) (dx < 0) ? -((-dx >> 1) + s) : ((dx >> 1) + s)
|
||||
|
||||
// Scale plane, 1/2
|
||||
// This is an optimized version for scaling down a plane to 1/2 of
|
||||
// its original size.
|
||||
|
||||
static void ScalePlaneDown2(int src_width,
|
||||
int src_height,
|
||||
int dst_width,
|
||||
int dst_height,
|
||||
int src_stride,
|
||||
int dst_stride,
|
||||
const uint8_t *src_ptr,
|
||||
uint8_t *dst_ptr,
|
||||
enum FilterMode filtering) {
|
||||
int y;
|
||||
void (*ScaleRowDown2)(const uint8_t *src_ptr, ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr, int dst_width) =
|
||||
filtering == kFilterNone
|
||||
? ScaleRowDown2_C
|
||||
: (filtering == kFilterLinear ? ScaleRowDown2Linear_C
|
||||
: ScaleRowDown2Box_C);
|
||||
int row_stride = src_stride * 2;
|
||||
(void) src_width;
|
||||
(void) src_height;
|
||||
if (!filtering) {
|
||||
src_ptr += src_stride; // Point to odd rows.
|
||||
src_stride = 0;
|
||||
}
|
||||
|
||||
|
||||
#if defined(HAS_SCALEROWDOWN2_SSSE3)
|
||||
if (TestCpuFlag(kCpuHasSSSE3)) {
|
||||
ScaleRowDown2 =
|
||||
filtering == kFilterNone
|
||||
? ScaleRowDown2_Any_SSSE3
|
||||
: (filtering == kFilterLinear ? ScaleRowDown2Linear_Any_SSSE3
|
||||
: ScaleRowDown2Box_Any_SSSE3);
|
||||
if (IS_ALIGNED(dst_width, 16)) {
|
||||
ScaleRowDown2 =
|
||||
filtering == kFilterNone
|
||||
? ScaleRowDown2_SSSE3
|
||||
: (filtering == kFilterLinear ? ScaleRowDown2Linear_SSSE3
|
||||
: ScaleRowDown2Box_SSSE3);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(HAS_SCALEROWDOWN2_AVX2)
|
||||
if (TestCpuFlag(kCpuHasAVX2)) {
|
||||
ScaleRowDown2 =
|
||||
filtering == kFilterNone
|
||||
? ScaleRowDown2_Any_AVX2
|
||||
: (filtering == kFilterLinear ? ScaleRowDown2Linear_Any_AVX2
|
||||
: ScaleRowDown2Box_Any_AVX2);
|
||||
if (IS_ALIGNED(dst_width, 32)) {
|
||||
ScaleRowDown2 = filtering == kFilterNone ? ScaleRowDown2_AVX2
|
||||
: (filtering == kFilterLinear
|
||||
? ScaleRowDown2Linear_AVX2
|
||||
: ScaleRowDown2Box_AVX2);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (filtering == kFilterLinear) {
|
||||
src_stride = 0;
|
||||
}
|
||||
// TODO(fbarchard): Loop through source height to allow odd height.
|
||||
for (y = 0; y < dst_height; ++y) {
|
||||
ScaleRowDown2(src_ptr, src_stride, dst_ptr, dst_width);
|
||||
src_ptr += row_stride;
|
||||
dst_ptr += dst_stride;
|
||||
}
|
||||
}
|
||||
|
||||
// Scale plane, 1/4
|
||||
// This is an optimized version for scaling down a plane to 1/4 of
|
||||
// its original size.
|
||||
|
||||
static void ScalePlaneDown4(int src_width,
|
||||
int src_height,
|
||||
int dst_width,
|
||||
int dst_height,
|
||||
int src_stride,
|
||||
int dst_stride,
|
||||
const uint8_t *src_ptr,
|
||||
uint8_t *dst_ptr,
|
||||
enum FilterMode filtering) {
|
||||
int y;
|
||||
void (*ScaleRowDown4)(const uint8_t *src_ptr, ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr, int dst_width) =
|
||||
filtering ? ScaleRowDown4Box_C : ScaleRowDown4_C;
|
||||
int row_stride = src_stride * 4;
|
||||
(void) src_width;
|
||||
(void) src_height;
|
||||
if (!filtering) {
|
||||
src_ptr += src_stride * 2; // Point to row 2.
|
||||
src_stride = 0;
|
||||
}
|
||||
|
||||
#if defined(HAS_SCALEROWDOWN4_SSSE3)
|
||||
if (TestCpuFlag(kCpuHasSSSE3)) {
|
||||
ScaleRowDown4 =
|
||||
filtering ? ScaleRowDown4Box_Any_SSSE3 : ScaleRowDown4_Any_SSSE3;
|
||||
if (IS_ALIGNED(dst_width, 8)) {
|
||||
ScaleRowDown4 = filtering ? ScaleRowDown4Box_SSSE3 : ScaleRowDown4_SSSE3;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(HAS_SCALEROWDOWN4_AVX2)
|
||||
if (TestCpuFlag(kCpuHasAVX2)) {
|
||||
ScaleRowDown4 =
|
||||
filtering ? ScaleRowDown4Box_Any_AVX2 : ScaleRowDown4_Any_AVX2;
|
||||
if (IS_ALIGNED(dst_width, 16)) {
|
||||
ScaleRowDown4 = filtering ? ScaleRowDown4Box_AVX2 : ScaleRowDown4_AVX2;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (filtering == kFilterLinear) {
|
||||
src_stride = 0;
|
||||
}
|
||||
for (y = 0; y < dst_height; ++y) {
|
||||
ScaleRowDown4(src_ptr, src_stride, dst_ptr, dst_width);
|
||||
src_ptr += row_stride;
|
||||
dst_ptr += dst_stride;
|
||||
}
|
||||
}
|
||||
|
||||
// Scale plane down, 3/4
|
||||
static void ScalePlaneDown34(int src_width,
|
||||
int src_height,
|
||||
int dst_width,
|
||||
int dst_height,
|
||||
int src_stride,
|
||||
int dst_stride,
|
||||
const uint8_t *src_ptr,
|
||||
uint8_t *dst_ptr,
|
||||
enum FilterMode filtering) {
|
||||
int y;
|
||||
void (*ScaleRowDown34_0)(const uint8_t *src_ptr, ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr, int dst_width);
|
||||
void (*ScaleRowDown34_1)(const uint8_t *src_ptr, ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr, int dst_width);
|
||||
const int filter_stride = (filtering == kFilterLinear) ? 0 : src_stride;
|
||||
(void) src_width;
|
||||
(void) src_height;
|
||||
assert(dst_width % 3 == 0);
|
||||
if (!filtering) {
|
||||
ScaleRowDown34_0 = ScaleRowDown34_C;
|
||||
ScaleRowDown34_1 = ScaleRowDown34_C;
|
||||
} else {
|
||||
ScaleRowDown34_0 = ScaleRowDown34_0_Box_C;
|
||||
ScaleRowDown34_1 = ScaleRowDown34_1_Box_C;
|
||||
}
|
||||
|
||||
#if defined(HAS_SCALEROWDOWN34_SSSE3)
|
||||
if (TestCpuFlag(kCpuHasSSSE3)) {
|
||||
if (!filtering) {
|
||||
ScaleRowDown34_0 = ScaleRowDown34_Any_SSSE3;
|
||||
ScaleRowDown34_1 = ScaleRowDown34_Any_SSSE3;
|
||||
} else {
|
||||
ScaleRowDown34_0 = ScaleRowDown34_0_Box_Any_SSSE3;
|
||||
ScaleRowDown34_1 = ScaleRowDown34_1_Box_Any_SSSE3;
|
||||
}
|
||||
if (dst_width % 24 == 0) {
|
||||
if (!filtering) {
|
||||
ScaleRowDown34_0 = ScaleRowDown34_SSSE3;
|
||||
ScaleRowDown34_1 = ScaleRowDown34_SSSE3;
|
||||
} else {
|
||||
ScaleRowDown34_0 = ScaleRowDown34_0_Box_SSSE3;
|
||||
ScaleRowDown34_1 = ScaleRowDown34_1_Box_SSSE3;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
for (y = 0; y < dst_height - 2; y += 3) {
|
||||
ScaleRowDown34_0(src_ptr, filter_stride, dst_ptr, dst_width);
|
||||
src_ptr += src_stride;
|
||||
dst_ptr += dst_stride;
|
||||
ScaleRowDown34_1(src_ptr, filter_stride, dst_ptr, dst_width);
|
||||
src_ptr += src_stride;
|
||||
dst_ptr += dst_stride;
|
||||
ScaleRowDown34_0(src_ptr + src_stride, -filter_stride, dst_ptr, dst_width);
|
||||
src_ptr += src_stride * 2;
|
||||
dst_ptr += dst_stride;
|
||||
}
|
||||
|
||||
// Remainder 1 or 2 rows with last row vertically unfiltered
|
||||
if ((dst_height % 3) == 2) {
|
||||
ScaleRowDown34_0(src_ptr, filter_stride, dst_ptr, dst_width);
|
||||
src_ptr += src_stride;
|
||||
dst_ptr += dst_stride;
|
||||
ScaleRowDown34_1(src_ptr, 0, dst_ptr, dst_width);
|
||||
} else if ((dst_height % 3) == 1) {
|
||||
ScaleRowDown34_0(src_ptr, 0, dst_ptr, dst_width);
|
||||
}
|
||||
}
|
||||
|
||||
// Scale plane, 3/8
|
||||
// This is an optimized version for scaling down a plane to 3/8
|
||||
// of its original size.
|
||||
//
|
||||
// Uses box filter arranges like this
|
||||
// aaabbbcc -> abc
|
||||
// aaabbbcc def
|
||||
// aaabbbcc ghi
|
||||
// dddeeeff
|
||||
// dddeeeff
|
||||
// dddeeeff
|
||||
// ggghhhii
|
||||
// ggghhhii
|
||||
// Boxes are 3x3, 2x3, 3x2 and 2x2
|
||||
|
||||
static void ScalePlaneDown38(int src_width,
|
||||
int src_height,
|
||||
int dst_width,
|
||||
int dst_height,
|
||||
int src_stride,
|
||||
int dst_stride,
|
||||
const uint8_t *src_ptr,
|
||||
uint8_t *dst_ptr,
|
||||
enum FilterMode filtering) {
|
||||
int y;
|
||||
void (*ScaleRowDown38_3)(const uint8_t *src_ptr, ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr, int dst_width);
|
||||
void (*ScaleRowDown38_2)(const uint8_t *src_ptr, ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr, int dst_width);
|
||||
const int filter_stride = (filtering == kFilterLinear) ? 0 : src_stride;
|
||||
assert(dst_width % 3 == 0);
|
||||
(void) src_width;
|
||||
(void) src_height;
|
||||
if (!filtering) {
|
||||
ScaleRowDown38_3 = ScaleRowDown38_C;
|
||||
ScaleRowDown38_2 = ScaleRowDown38_C;
|
||||
} else {
|
||||
ScaleRowDown38_3 = ScaleRowDown38_3_Box_C;
|
||||
ScaleRowDown38_2 = ScaleRowDown38_2_Box_C;
|
||||
}
|
||||
|
||||
#if defined(HAS_SCALEROWDOWN38_SSSE3)
|
||||
if (TestCpuFlag(kCpuHasSSSE3)) {
|
||||
if (!filtering) {
|
||||
ScaleRowDown38_3 = ScaleRowDown38_Any_SSSE3;
|
||||
ScaleRowDown38_2 = ScaleRowDown38_Any_SSSE3;
|
||||
} else {
|
||||
ScaleRowDown38_3 = ScaleRowDown38_3_Box_Any_SSSE3;
|
||||
ScaleRowDown38_2 = ScaleRowDown38_2_Box_Any_SSSE3;
|
||||
}
|
||||
if (dst_width % 12 == 0 && !filtering) {
|
||||
ScaleRowDown38_3 = ScaleRowDown38_SSSE3;
|
||||
ScaleRowDown38_2 = ScaleRowDown38_SSSE3;
|
||||
}
|
||||
if (dst_width % 6 == 0 && filtering) {
|
||||
ScaleRowDown38_3 = ScaleRowDown38_3_Box_SSSE3;
|
||||
ScaleRowDown38_2 = ScaleRowDown38_2_Box_SSSE3;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
for (y = 0; y < dst_height - 2; y += 3) {
|
||||
ScaleRowDown38_3(src_ptr, filter_stride, dst_ptr, dst_width);
|
||||
src_ptr += src_stride * 3;
|
||||
dst_ptr += dst_stride;
|
||||
ScaleRowDown38_3(src_ptr, filter_stride, dst_ptr, dst_width);
|
||||
src_ptr += src_stride * 3;
|
||||
dst_ptr += dst_stride;
|
||||
ScaleRowDown38_2(src_ptr, filter_stride, dst_ptr, dst_width);
|
||||
src_ptr += src_stride * 2;
|
||||
dst_ptr += dst_stride;
|
||||
}
|
||||
|
||||
// Remainder 1 or 2 rows with last row vertically unfiltered
|
||||
if ((dst_height % 3) == 2) {
|
||||
ScaleRowDown38_3(src_ptr, filter_stride, dst_ptr, dst_width);
|
||||
src_ptr += src_stride * 3;
|
||||
dst_ptr += dst_stride;
|
||||
ScaleRowDown38_3(src_ptr, 0, dst_ptr, dst_width);
|
||||
} else if ((dst_height % 3) == 1) {
|
||||
ScaleRowDown38_3(src_ptr, 0, dst_ptr, dst_width);
|
||||
}
|
||||
}
|
||||
|
||||
#define MIN1(x) ((x) < 1 ? 1 : (x))
|
||||
|
||||
static __inline uint32_t SumPixels(int iboxwidth, const uint16_t *src_ptr) {
|
||||
uint32_t sum = 0u;
|
||||
int x;
|
||||
assert(iboxwidth > 0);
|
||||
for (x = 0; x < iboxwidth; ++x) {
|
||||
sum += src_ptr[x];
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
static __inline uint32_t SumPixels_16(int iboxwidth, const uint32_t *src_ptr) {
|
||||
uint32_t sum = 0u;
|
||||
int x;
|
||||
assert(iboxwidth > 0);
|
||||
for (x = 0; x < iboxwidth; ++x) {
|
||||
sum += src_ptr[x];
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
static void ScaleAddCols2_C(int dst_width,
|
||||
int boxheight,
|
||||
int x,
|
||||
int dx,
|
||||
const uint16_t *src_ptr,
|
||||
uint8_t *dst_ptr) {
|
||||
int i;
|
||||
int scaletbl[2];
|
||||
int minboxwidth = dx >> 16;
|
||||
int boxwidth;
|
||||
scaletbl[0] = 65536 / (MIN1(minboxwidth) * boxheight);
|
||||
scaletbl[1] = 65536 / (MIN1(minboxwidth + 1) * boxheight);
|
||||
for (i = 0; i < dst_width; ++i) {
|
||||
int ix = x >> 16;
|
||||
x += dx;
|
||||
boxwidth = MIN1((x >> 16) - ix);
|
||||
int scaletbl_index = boxwidth - minboxwidth;
|
||||
assert((scaletbl_index == 0) || (scaletbl_index == 1));
|
||||
*dst_ptr++ = (uint8_t) (SumPixels(boxwidth, src_ptr + ix) *
|
||||
scaletbl[scaletbl_index] >>
|
||||
16);
|
||||
}
|
||||
}
|
||||
|
||||
static void ScaleAddCols0_C(int dst_width,
|
||||
int boxheight,
|
||||
int x,
|
||||
int dx,
|
||||
const uint16_t *src_ptr,
|
||||
uint8_t *dst_ptr) {
|
||||
int scaleval = 65536 / boxheight;
|
||||
int i;
|
||||
(void) dx;
|
||||
src_ptr += (x >> 16);
|
||||
for (i = 0; i < dst_width; ++i) {
|
||||
*dst_ptr++ = (uint8_t) (src_ptr[i] * scaleval >> 16);
|
||||
}
|
||||
}
|
||||
|
||||
static void ScaleAddCols1_C(int dst_width,
|
||||
int boxheight,
|
||||
int x,
|
||||
int dx,
|
||||
const uint16_t *src_ptr,
|
||||
uint8_t *dst_ptr) {
|
||||
int boxwidth = MIN1(dx >> 16);
|
||||
int scaleval = 65536 / (boxwidth * boxheight);
|
||||
int i;
|
||||
x >>= 16;
|
||||
for (i = 0; i < dst_width; ++i) {
|
||||
*dst_ptr++ = (uint8_t) (SumPixels(boxwidth, src_ptr + x) * scaleval >> 16);
|
||||
x += boxwidth;
|
||||
}
|
||||
}
|
||||
|
||||
// Scale plane down to any dimensions, with interpolation.
|
||||
// (boxfilter).
|
||||
//
|
||||
// Same method as SimpleScale, which is fixed point, outputting
|
||||
// one pixel of destination using fixed point (16.16) to step
|
||||
// through source, sampling a box of pixel with simple
|
||||
// averaging.
|
||||
static void ScalePlaneBox(int src_width,
|
||||
int src_height,
|
||||
int dst_width,
|
||||
int dst_height,
|
||||
int src_stride,
|
||||
int dst_stride,
|
||||
const uint8_t *src_ptr,
|
||||
uint8_t *dst_ptr) {
|
||||
int j, k;
|
||||
// Initial source x/y coordinate and step values as 16.16 fixed point.
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int dx = 0;
|
||||
int dy = 0;
|
||||
const int max_y = (src_height << 16);
|
||||
ScaleSlope(src_width, src_height, dst_width, dst_height, kFilterBox, &x, &y,
|
||||
&dx, &dy);
|
||||
src_width = Abs(src_width);
|
||||
{
|
||||
// Allocate a row buffer of uint16_t.
|
||||
align_buffer_64(row16, src_width * 2);
|
||||
void (*ScaleAddCols)(int dst_width, int boxheight, int x, int dx,
|
||||
const uint16_t *src_ptr, uint8_t *dst_ptr) =
|
||||
(dx & 0xffff) ? ScaleAddCols2_C
|
||||
: ((dx != 0x10000) ? ScaleAddCols1_C : ScaleAddCols0_C);
|
||||
void (*ScaleAddRow)(const uint8_t *src_ptr, uint16_t *dst_ptr,
|
||||
int src_width) = ScaleAddRow_C;
|
||||
#if defined(HAS_SCALEADDROW_SSE2)
|
||||
if (TestCpuFlag(kCpuHasSSE2)) {
|
||||
ScaleAddRow = ScaleAddRow_Any_SSE2;
|
||||
if (IS_ALIGNED(src_width, 16)) {
|
||||
ScaleAddRow = ScaleAddRow_SSE2;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(HAS_SCALEADDROW_AVX2)
|
||||
if (TestCpuFlag(kCpuHasAVX2)) {
|
||||
ScaleAddRow = ScaleAddRow_Any_AVX2;
|
||||
if (IS_ALIGNED(src_width, 32)) {
|
||||
ScaleAddRow = ScaleAddRow_AVX2;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
for (j = 0; j < dst_height; ++j) {
|
||||
int boxheight;
|
||||
int iy = y >> 16;
|
||||
const uint8_t *src = src_ptr + iy * (int64_t) src_stride;
|
||||
y += dy;
|
||||
if (y > max_y) {
|
||||
y = max_y;
|
||||
}
|
||||
boxheight = MIN1((y >> 16) - iy);
|
||||
memset(row16, 0, src_width * 2);
|
||||
for (k = 0; k < boxheight; ++k) {
|
||||
ScaleAddRow(src, (uint16_t *) (row16), src_width);
|
||||
src += src_stride;
|
||||
}
|
||||
ScaleAddCols(dst_width, boxheight, x, dx, (uint16_t *) (row16), dst_ptr);
|
||||
dst_ptr += dst_stride;
|
||||
}
|
||||
free_aligned_buffer_64(row16);
|
||||
}
|
||||
}
|
||||
|
||||
// Scale plane down with bilinear interpolation.
|
||||
static void ScalePlaneBilinearDown(int src_width,
|
||||
int src_height,
|
||||
int dst_width,
|
||||
int dst_height,
|
||||
int src_stride,
|
||||
int dst_stride,
|
||||
const uint8_t *src_ptr,
|
||||
uint8_t *dst_ptr,
|
||||
enum FilterMode filtering) {
|
||||
// Initial source x/y coordinate and step values as 16.16 fixed point.
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int dx = 0;
|
||||
int dy = 0;
|
||||
// TODO(fbarchard): Consider not allocating row buffer for kFilterLinear.
|
||||
// Allocate a row buffer.
|
||||
align_buffer_64(row, src_width);
|
||||
|
||||
const int max_y = (src_height - 1) << 16;
|
||||
int j;
|
||||
void (*ScaleFilterCols)(uint8_t *dst_ptr, const uint8_t *src_ptr,
|
||||
int dst_width, int x, int dx) =
|
||||
(src_width >= 32768) ? ScaleFilterCols64_C : ScaleFilterCols_C;
|
||||
void (*InterpolateRow)(uint8_t *dst_ptr, const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride, int dst_width,
|
||||
int source_y_fraction) = InterpolateRow_C;
|
||||
ScaleSlope(src_width, src_height, dst_width, dst_height, filtering, &x, &y,
|
||||
&dx, &dy);
|
||||
src_width = Abs(src_width);
|
||||
|
||||
#if defined(HAS_INTERPOLATEROW_SSSE3)
|
||||
if (TestCpuFlag(kCpuHasSSSE3)) {
|
||||
InterpolateRow = InterpolateRow_Any_SSSE3;
|
||||
if (IS_ALIGNED(src_width, 16)) {
|
||||
InterpolateRow = InterpolateRow_SSSE3;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(HAS_INTERPOLATEROW_AVX2)
|
||||
if (TestCpuFlag(kCpuHasAVX2)) {
|
||||
InterpolateRow = InterpolateRow_Any_AVX2;
|
||||
if (IS_ALIGNED(src_width, 32)) {
|
||||
InterpolateRow = InterpolateRow_AVX2;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAS_SCALEFILTERCOLS_SSSE3)
|
||||
if (TestCpuFlag(kCpuHasSSSE3) && src_width < 32768) {
|
||||
ScaleFilterCols = ScaleFilterCols_SSSE3;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (y > max_y) {
|
||||
y = max_y;
|
||||
}
|
||||
|
||||
for (j = 0; j < dst_height; ++j) {
|
||||
int yi = y >> 16;
|
||||
const uint8_t *src = src_ptr + yi * (int64_t) src_stride;
|
||||
if (filtering == kFilterLinear) {
|
||||
ScaleFilterCols(dst_ptr, src, dst_width, x, dx);
|
||||
} else {
|
||||
int yf = (y >> 8) & 255;
|
||||
InterpolateRow(row, src, src_stride, src_width, yf);
|
||||
ScaleFilterCols(dst_ptr, row, dst_width, x, dx);
|
||||
}
|
||||
dst_ptr += dst_stride;
|
||||
y += dy;
|
||||
if (y > max_y) {
|
||||
y = max_y;
|
||||
}
|
||||
}
|
||||
free_aligned_buffer_64(row);
|
||||
}
|
||||
|
||||
// Scale up down with bilinear interpolation.
|
||||
static void ScalePlaneBilinearUp(int src_width,
|
||||
int src_height,
|
||||
int dst_width,
|
||||
int dst_height,
|
||||
int src_stride,
|
||||
int dst_stride,
|
||||
const uint8_t *src_ptr,
|
||||
uint8_t *dst_ptr,
|
||||
enum FilterMode filtering) {
|
||||
int j;
|
||||
// Initial source x/y coordinate and step values as 16.16 fixed point.
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int dx = 0;
|
||||
int dy = 0;
|
||||
const int max_y = (src_height - 1) << 16;
|
||||
void (*InterpolateRow)(uint8_t *dst_ptr, const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride, int dst_width,
|
||||
int source_y_fraction) = InterpolateRow_C;
|
||||
void (*ScaleFilterCols)(uint8_t *dst_ptr, const uint8_t *src_ptr,
|
||||
int dst_width, int x, int dx) =
|
||||
filtering ? ScaleFilterCols_C : ScaleCols_C;
|
||||
ScaleSlope(src_width, src_height, dst_width, dst_height, filtering, &x, &y,
|
||||
&dx, &dy);
|
||||
src_width = Abs(src_width);
|
||||
|
||||
#if defined(HAS_INTERPOLATEROW_SSSE3)
|
||||
if (TestCpuFlag(kCpuHasSSSE3)) {
|
||||
InterpolateRow = InterpolateRow_Any_SSSE3;
|
||||
if (IS_ALIGNED(dst_width, 16)) {
|
||||
InterpolateRow = InterpolateRow_SSSE3;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(HAS_INTERPOLATEROW_AVX2)
|
||||
if (TestCpuFlag(kCpuHasAVX2)) {
|
||||
InterpolateRow = InterpolateRow_Any_AVX2;
|
||||
if (IS_ALIGNED(dst_width, 32)) {
|
||||
InterpolateRow = InterpolateRow_AVX2;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (filtering && src_width >= 32768) {
|
||||
ScaleFilterCols = ScaleFilterCols64_C;
|
||||
}
|
||||
#if defined(HAS_SCALEFILTERCOLS_SSSE3)
|
||||
if (filtering && TestCpuFlag(kCpuHasSSSE3) && src_width < 32768) {
|
||||
ScaleFilterCols = ScaleFilterCols_SSSE3;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!filtering && src_width * 2 == dst_width && x < 0x8000) {
|
||||
ScaleFilterCols = ScaleColsUp2_C;
|
||||
#if defined(HAS_SCALECOLS_SSE2)
|
||||
if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(dst_width, 8)) {
|
||||
ScaleFilterCols = ScaleColsUp2_SSE2;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (y > max_y) {
|
||||
y = max_y;
|
||||
}
|
||||
{
|
||||
int yi = y >> 16;
|
||||
const uint8_t *src = src_ptr + yi * (int64_t) src_stride;
|
||||
|
||||
// Allocate 2 row buffers.
|
||||
const int row_size = (dst_width + 31) & ~31;
|
||||
align_buffer_64(row, row_size * 2);
|
||||
|
||||
uint8_t *rowptr = row;
|
||||
int rowstride = row_size;
|
||||
int lasty = yi;
|
||||
|
||||
ScaleFilterCols(rowptr, src, dst_width, x, dx);
|
||||
if (src_height > 1) {
|
||||
src += src_stride;
|
||||
}
|
||||
ScaleFilterCols(rowptr + rowstride, src, dst_width, x, dx);
|
||||
if (src_height > 2) {
|
||||
src += src_stride;
|
||||
}
|
||||
|
||||
for (j = 0; j < dst_height; ++j) {
|
||||
yi = y >> 16;
|
||||
if (yi != lasty) {
|
||||
if (y > max_y) {
|
||||
y = max_y;
|
||||
yi = y >> 16;
|
||||
src = src_ptr + yi * (int64_t) src_stride;
|
||||
}
|
||||
if (yi != lasty) {
|
||||
ScaleFilterCols(rowptr, src, dst_width, x, dx);
|
||||
rowptr += rowstride;
|
||||
rowstride = -rowstride;
|
||||
lasty = yi;
|
||||
if ((y + 65536) < max_y) {
|
||||
src += src_stride;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (filtering == kFilterLinear) {
|
||||
InterpolateRow(dst_ptr, rowptr, 0, dst_width, 0);
|
||||
} else {
|
||||
int yf = (y >> 8) & 255;
|
||||
InterpolateRow(dst_ptr, rowptr, rowstride, dst_width, yf);
|
||||
}
|
||||
dst_ptr += dst_stride;
|
||||
y += dy;
|
||||
}
|
||||
free_aligned_buffer_64(row);
|
||||
}
|
||||
}
|
||||
|
||||
// Scale plane, horizontally up by 2 times.
|
||||
// Uses linear filter horizontally, nearest vertically.
|
||||
// This is an optimized version for scaling up a plane to 2 times of
|
||||
// its original width, using linear interpolation.
|
||||
// This is used to scale U and V planes of I422 to I444.
|
||||
static void ScalePlaneUp2_Linear(int src_width,
|
||||
int src_height,
|
||||
int dst_width,
|
||||
int dst_height,
|
||||
int src_stride,
|
||||
int dst_stride,
|
||||
const uint8_t *src_ptr,
|
||||
uint8_t *dst_ptr) {
|
||||
void (*ScaleRowUp)(const uint8_t *src_ptr, uint8_t *dst_ptr, int dst_width) =
|
||||
ScaleRowUp2_Linear_Any_C;
|
||||
int i;
|
||||
int y;
|
||||
int dy;
|
||||
|
||||
(void) src_width;
|
||||
// This function can only scale up by 2 times horizontally.
|
||||
assert(src_width == ((dst_width + 1) / 2));
|
||||
|
||||
#ifdef HAS_SCALEROWUP2_LINEAR_SSE2
|
||||
if (TestCpuFlag(kCpuHasSSE2)) {
|
||||
ScaleRowUp = ScaleRowUp2_Linear_Any_SSE2;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SCALEROWUP2_LINEAR_SSSE3
|
||||
if (TestCpuFlag(kCpuHasSSSE3)) {
|
||||
ScaleRowUp = ScaleRowUp2_Linear_Any_SSSE3;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SCALEROWUP2_LINEAR_AVX2
|
||||
if (TestCpuFlag(kCpuHasAVX2)) {
|
||||
ScaleRowUp = ScaleRowUp2_Linear_Any_AVX2;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
if (dst_height == 1) {
|
||||
ScaleRowUp(src_ptr + ((src_height - 1) / 2) * (int64_t) src_stride, dst_ptr,
|
||||
dst_width);
|
||||
} else {
|
||||
dy = FixedDiv(src_height - 1, dst_height - 1);
|
||||
y = (1 << 15) - 1;
|
||||
for (i = 0; i < dst_height; ++i) {
|
||||
ScaleRowUp(src_ptr + (y >> 16) * (int64_t) src_stride, dst_ptr, dst_width);
|
||||
dst_ptr += dst_stride;
|
||||
y += dy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scale plane, up by 2 times.
|
||||
// This is an optimized version for scaling up a plane to 2 times of
|
||||
// its original size, using bilinear interpolation.
|
||||
// This is used to scale U and V planes of I420 to I444.
|
||||
static void ScalePlaneUp2_Bilinear(int src_width,
|
||||
int src_height,
|
||||
int dst_width,
|
||||
int dst_height,
|
||||
int src_stride,
|
||||
int dst_stride,
|
||||
const uint8_t *src_ptr,
|
||||
uint8_t *dst_ptr) {
|
||||
void (*Scale2RowUp)(const uint8_t *src_ptr, ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr, ptrdiff_t dst_stride, int dst_width) =
|
||||
ScaleRowUp2_Bilinear_Any_C;
|
||||
int x;
|
||||
|
||||
(void) src_width;
|
||||
// This function can only scale up by 2 times.
|
||||
assert(src_width == ((dst_width + 1) / 2));
|
||||
assert(src_height == ((dst_height + 1) / 2));
|
||||
|
||||
#ifdef HAS_SCALEROWUP2_BILINEAR_SSE2
|
||||
if (TestCpuFlag(kCpuHasSSE2)) {
|
||||
Scale2RowUp = ScaleRowUp2_Bilinear_Any_SSE2;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SCALEROWUP2_BILINEAR_SSSE3
|
||||
if (TestCpuFlag(kCpuHasSSSE3)) {
|
||||
Scale2RowUp = ScaleRowUp2_Bilinear_Any_SSSE3;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SCALEROWUP2_BILINEAR_AVX2
|
||||
if (TestCpuFlag(kCpuHasAVX2)) {
|
||||
Scale2RowUp = ScaleRowUp2_Bilinear_Any_AVX2;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
Scale2RowUp(src_ptr, 0, dst_ptr, 0, dst_width);
|
||||
dst_ptr += dst_stride;
|
||||
for (x = 0; x < src_height - 1; ++x) {
|
||||
Scale2RowUp(src_ptr, src_stride, dst_ptr, dst_stride, dst_width);
|
||||
src_ptr += src_stride;
|
||||
// TODO(fbarchard): Test performance of writing one row of destination at a
|
||||
// time.
|
||||
dst_ptr += 2 * dst_stride;
|
||||
}
|
||||
if (!(dst_height & 1)) {
|
||||
Scale2RowUp(src_ptr, 0, dst_ptr, 0, dst_width);
|
||||
}
|
||||
}
|
||||
|
||||
// Scale Plane to/from any dimensions, without interpolation.
|
||||
// Fixed point math is used for performance: The upper 16 bits
|
||||
// of x and dx is the integer part of the source position and
|
||||
// the lower 16 bits are the fixed decimal part.
|
||||
|
||||
static void ScalePlaneSimple(int src_width,
|
||||
int src_height,
|
||||
int dst_width,
|
||||
int dst_height,
|
||||
int src_stride,
|
||||
int dst_stride,
|
||||
const uint8_t *src_ptr,
|
||||
uint8_t *dst_ptr) {
|
||||
int i;
|
||||
void (*ScaleCols)(uint8_t *dst_ptr, const uint8_t *src_ptr, int dst_width,
|
||||
int x, int dx) = ScaleCols_C;
|
||||
// Initial source x/y coordinate and step values as 16.16 fixed point.
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
int dx = 0;
|
||||
int dy = 0;
|
||||
ScaleSlope(src_width, src_height, dst_width, dst_height, kFilterNone, &x, &y,
|
||||
&dx, &dy);
|
||||
src_width = Abs(src_width);
|
||||
|
||||
if (src_width * 2 == dst_width && x < 0x8000) {
|
||||
ScaleCols = ScaleColsUp2_C;
|
||||
#if defined(HAS_SCALECOLS_SSE2)
|
||||
if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(dst_width, 8)) {
|
||||
ScaleCols = ScaleColsUp2_SSE2;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
for (i = 0; i < dst_height; ++i) {
|
||||
ScaleCols(dst_ptr, src_ptr + (y >> 16) * (int64_t) src_stride, dst_width, x,
|
||||
dx);
|
||||
dst_ptr += dst_stride;
|
||||
y += dy;
|
||||
}
|
||||
}
|
||||
|
||||
// Scale a plane.
|
||||
// This function dispatches to a specialized scaler based on scale factor.
|
||||
LIBYUV_API
|
||||
void ScalePlane(const uint8_t *src,
|
||||
int src_stride,
|
||||
int src_width,
|
||||
int src_height,
|
||||
uint8_t *dst,
|
||||
int dst_stride,
|
||||
int dst_width,
|
||||
int dst_height,
|
||||
enum FilterMode filtering) {
|
||||
// Simplify filtering when possible.
|
||||
filtering = ScaleFilterReduce(src_width, src_height, dst_width, dst_height,
|
||||
filtering);
|
||||
|
||||
// Negative height means invert the image.
|
||||
if (src_height < 0) {
|
||||
src_height = -src_height;
|
||||
src = src + (src_height - 1) * (int64_t) src_stride;
|
||||
src_stride = -src_stride;
|
||||
}
|
||||
// Use specialized scales to improve performance for common resolutions.
|
||||
// For example, all the 1/2 scalings will use ScalePlaneDown2()
|
||||
if (dst_width == src_width && dst_height == src_height) {
|
||||
// Straight copy.
|
||||
CopyPlane(src, src_stride, dst, dst_stride, dst_width, dst_height);
|
||||
return;
|
||||
}
|
||||
if (dst_width == src_width && filtering != kFilterBox) {
|
||||
int dy = 0;
|
||||
int y = 0;
|
||||
// When scaling down, use the center 2 rows to filter.
|
||||
// When scaling up, last row of destination uses the last 2 source rows.
|
||||
if (dst_height <= src_height) {
|
||||
dy = FixedDiv(src_height, dst_height);
|
||||
y = CENTERSTART(dy, -32768); // Subtract 0.5 (32768) to center filter.
|
||||
} else if (src_height > 1 && dst_height > 1) {
|
||||
dy = FixedDiv1(src_height, dst_height);
|
||||
}
|
||||
// Arbitrary scale vertically, but unscaled horizontally.
|
||||
ScalePlaneVertical(src_height, dst_width, dst_height, src_stride,
|
||||
dst_stride, src, dst, 0, y, dy, /*bpp=*/1, filtering);
|
||||
return;
|
||||
}
|
||||
if (dst_width <= Abs(src_width) && dst_height <= src_height) {
|
||||
// Scale down.
|
||||
if (4 * dst_width == 3 * src_width && 4 * dst_height == 3 * src_height) {
|
||||
// optimized, 3/4
|
||||
ScalePlaneDown34(src_width, src_height, dst_width, dst_height, src_stride,
|
||||
dst_stride, src, dst, filtering);
|
||||
return;
|
||||
}
|
||||
if (2 * dst_width == src_width && 2 * dst_height == src_height) {
|
||||
// optimized, 1/2
|
||||
ScalePlaneDown2(src_width, src_height, dst_width, dst_height, src_stride,
|
||||
dst_stride, src, dst, filtering);
|
||||
return;
|
||||
}
|
||||
// 3/8 rounded up for odd sized chroma height.
|
||||
if (8 * dst_width == 3 * src_width && 8 * dst_height == 3 * src_height) {
|
||||
// optimized, 3/8
|
||||
ScalePlaneDown38(src_width, src_height, dst_width, dst_height, src_stride,
|
||||
dst_stride, src, dst, filtering);
|
||||
return;
|
||||
}
|
||||
if (4 * dst_width == src_width && 4 * dst_height == src_height &&
|
||||
(filtering == kFilterBox || filtering == kFilterNone)) {
|
||||
// optimized, 1/4
|
||||
ScalePlaneDown4(src_width, src_height, dst_width, dst_height, src_stride,
|
||||
dst_stride, src, dst, filtering);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (filtering == kFilterBox && dst_height * 2 < src_height) {
|
||||
ScalePlaneBox(src_width, src_height, dst_width, dst_height, src_stride,
|
||||
dst_stride, src, dst);
|
||||
return;
|
||||
}
|
||||
if ((dst_width + 1) / 2 == src_width && filtering == kFilterLinear) {
|
||||
ScalePlaneUp2_Linear(src_width, src_height, dst_width, dst_height,
|
||||
src_stride, dst_stride, src, dst);
|
||||
return;
|
||||
}
|
||||
if ((dst_height + 1) / 2 == src_height && (dst_width + 1) / 2 == src_width &&
|
||||
(filtering == kFilterBilinear || filtering == kFilterBox)) {
|
||||
ScalePlaneUp2_Bilinear(src_width, src_height, dst_width, dst_height,
|
||||
src_stride, dst_stride, src, dst);
|
||||
return;
|
||||
}
|
||||
if (filtering && dst_height > src_height) {
|
||||
ScalePlaneBilinearUp(src_width, src_height, dst_width, dst_height,
|
||||
src_stride, dst_stride, src, dst, filtering);
|
||||
return;
|
||||
}
|
||||
if (filtering) {
|
||||
ScalePlaneBilinearDown(src_width, src_height, dst_width, dst_height,
|
||||
src_stride, dst_stride, src, dst, filtering);
|
||||
return;
|
||||
}
|
||||
ScalePlaneSimple(src_width, src_height, dst_width, dst_height, src_stride,
|
||||
dst_stride, src, dst);
|
||||
}
|
||||
|
||||
LIBYUV_API
|
||||
int I420Scale(const uint8_t *src_y,
|
||||
int src_stride_y,
|
||||
const uint8_t *src_u,
|
||||
int src_stride_u,
|
||||
const uint8_t *src_v,
|
||||
int src_stride_v,
|
||||
int src_width,
|
||||
int src_height,
|
||||
uint8_t *dst_y,
|
||||
int dst_stride_y,
|
||||
uint8_t *dst_u,
|
||||
int dst_stride_u,
|
||||
uint8_t *dst_v,
|
||||
int dst_stride_v,
|
||||
int dst_width,
|
||||
int dst_height,
|
||||
enum FilterMode filtering) {
|
||||
int src_halfwidth = SUBSAMPLE(src_width, 1, 1);
|
||||
int src_halfheight = SUBSAMPLE(src_height, 1, 1);
|
||||
int dst_halfwidth = SUBSAMPLE(dst_width, 1, 1);
|
||||
int dst_halfheight = SUBSAMPLE(dst_height, 1, 1);
|
||||
|
||||
if (!src_y || !src_u || !src_v || src_width <= 0 || src_height == 0 ||
|
||||
src_width > 32768 || src_height > 32768 || !dst_y || !dst_u || !dst_v ||
|
||||
dst_width <= 0 || dst_height <= 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ScalePlane(src_y, src_stride_y, src_width, src_height, dst_y, dst_stride_y,
|
||||
dst_width, dst_height, filtering);
|
||||
ScalePlane(src_u, src_stride_u, src_halfwidth, src_halfheight, dst_u,
|
||||
dst_stride_u, dst_halfwidth, dst_halfheight, filtering);
|
||||
ScalePlane(src_v, src_stride_v, src_halfwidth, src_halfheight, dst_v,
|
||||
dst_stride_v, dst_halfwidth, dst_halfheight, filtering);
|
||||
return 0;
|
||||
}
|
||||
53
pkg/encoder/yuv/libyuv/scale.h
Normal file
53
pkg/encoder/yuv/libyuv/scale.h
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright 2011 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_LIBYUV_SCALE_H_
|
||||
#define INCLUDE_LIBYUV_SCALE_H_
|
||||
|
||||
#include "basic_types.h"
|
||||
|
||||
// Supported filtering.
|
||||
typedef enum FilterMode {
|
||||
kFilterNone = 0, // Point sample; Fastest.
|
||||
kFilterLinear = 1, // Filter horizontally only.
|
||||
kFilterBilinear = 2, // Faster than box, but lower quality scaling down.
|
||||
kFilterBox = 3 // Highest quality.
|
||||
} FilterModeEnum;
|
||||
|
||||
// Scales a YUV 4:2:0 image from the src width and height to the
|
||||
// dst width and height.
|
||||
// If filtering is kFilterNone, a simple nearest-neighbor algorithm is
|
||||
// used. This produces basic (blocky) quality at the fastest speed.
|
||||
// If filtering is kFilterBilinear, interpolation is used to produce a better
|
||||
// quality image, at the expense of speed.
|
||||
// If filtering is kFilterBox, averaging is used to produce ever better
|
||||
// quality image, at further expense of speed.
|
||||
// Returns 0 if successful.
|
||||
|
||||
LIBYUV_API
|
||||
int I420Scale(const uint8_t *src_y,
|
||||
int src_stride_y,
|
||||
const uint8_t *src_u,
|
||||
int src_stride_u,
|
||||
const uint8_t *src_v,
|
||||
int src_stride_v,
|
||||
int src_width,
|
||||
int src_height,
|
||||
uint8_t *dst_y,
|
||||
int dst_stride_y,
|
||||
uint8_t *dst_u,
|
||||
int dst_stride_u,
|
||||
uint8_t *dst_v,
|
||||
int dst_stride_v,
|
||||
int dst_width,
|
||||
int dst_height,
|
||||
enum FilterMode filtering);
|
||||
|
||||
#endif // INCLUDE_LIBYUV_SCALE_H_
|
||||
632
pkg/encoder/yuv/libyuv/scale_any.c
Normal file
632
pkg/encoder/yuv/libyuv/scale_any.c
Normal file
|
|
@ -0,0 +1,632 @@
|
|||
/*
|
||||
* Copyright 2015 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "scale_row.h"
|
||||
|
||||
// Fixed scale down.
|
||||
// Mask may be non-power of 2, so use MOD
|
||||
#define SDANY(NAMEANY, SCALEROWDOWN_SIMD, SCALEROWDOWN_C, FACTOR, BPP, MASK) \
|
||||
void NAMEANY(const uint8_t* src_ptr, ptrdiff_t src_stride, uint8_t* dst_ptr, \
|
||||
int dst_width) { \
|
||||
int r = (int)((unsigned int)dst_width % (MASK + 1)); /* NOLINT */ \
|
||||
int n = dst_width - r; \
|
||||
if (n > 0) { \
|
||||
SCALEROWDOWN_SIMD(src_ptr, src_stride, dst_ptr, n); \
|
||||
} \
|
||||
SCALEROWDOWN_C(src_ptr + (n * FACTOR) * BPP, src_stride, \
|
||||
dst_ptr + n * BPP, r); \
|
||||
}
|
||||
|
||||
// Fixed scale down for odd source width. Used by I420Blend subsampling.
|
||||
// Since dst_width is (width + 1) / 2, this function scales one less pixel
|
||||
// and copies the last pixel.
|
||||
#define SDODD(NAMEANY, SCALEROWDOWN_SIMD, SCALEROWDOWN_C, FACTOR, BPP, MASK) \
|
||||
void NAMEANY(const uint8_t* src_ptr, ptrdiff_t src_stride, uint8_t* dst_ptr, \
|
||||
int dst_width) { \
|
||||
int r = (int)((unsigned int)(dst_width - 1) % (MASK + 1)); /* NOLINT */ \
|
||||
int n = (dst_width - 1) - r; \
|
||||
if (n > 0) { \
|
||||
SCALEROWDOWN_SIMD(src_ptr, src_stride, dst_ptr, n); \
|
||||
} \
|
||||
SCALEROWDOWN_C(src_ptr + (n * FACTOR) * BPP, src_stride, \
|
||||
dst_ptr + n * BPP, r + 1); \
|
||||
}
|
||||
|
||||
#ifdef HAS_SCALEROWDOWN2_SSSE3
|
||||
|
||||
SDANY(ScaleRowDown2_Any_SSSE3, ScaleRowDown2_SSSE3, ScaleRowDown2_C, 2, 1, 15)
|
||||
|
||||
SDANY(ScaleRowDown2Linear_Any_SSSE3,
|
||||
ScaleRowDown2Linear_SSSE3,
|
||||
ScaleRowDown2Linear_C,
|
||||
2,
|
||||
1,
|
||||
15)
|
||||
|
||||
SDANY(ScaleRowDown2Box_Any_SSSE3,
|
||||
ScaleRowDown2Box_SSSE3,
|
||||
ScaleRowDown2Box_C,
|
||||
2,
|
||||
1,
|
||||
15)
|
||||
|
||||
SDODD(ScaleRowDown2Box_Odd_SSSE3,
|
||||
ScaleRowDown2Box_SSSE3,
|
||||
ScaleRowDown2Box_Odd_C,
|
||||
2,
|
||||
1,
|
||||
15)
|
||||
|
||||
#endif
|
||||
#ifdef HAS_SCALEUVROWDOWN2BOX_SSSE3
|
||||
|
||||
SDANY(ScaleUVRowDown2Box_Any_SSSE3,
|
||||
ScaleUVRowDown2Box_SSSE3,
|
||||
ScaleUVRowDown2Box_C,
|
||||
2,
|
||||
2,
|
||||
3)
|
||||
|
||||
#endif
|
||||
#ifdef HAS_SCALEUVROWDOWN2BOX_AVX2
|
||||
|
||||
SDANY(ScaleUVRowDown2Box_Any_AVX2,
|
||||
ScaleUVRowDown2Box_AVX2,
|
||||
ScaleUVRowDown2Box_C,
|
||||
2,
|
||||
2,
|
||||
7)
|
||||
|
||||
#endif
|
||||
#ifdef HAS_SCALEROWDOWN2_AVX2
|
||||
|
||||
SDANY(ScaleRowDown2_Any_AVX2, ScaleRowDown2_AVX2, ScaleRowDown2_C, 2, 1, 31)
|
||||
|
||||
SDANY(ScaleRowDown2Linear_Any_AVX2,
|
||||
ScaleRowDown2Linear_AVX2,
|
||||
ScaleRowDown2Linear_C,
|
||||
2,
|
||||
1,
|
||||
31)
|
||||
|
||||
SDANY(ScaleRowDown2Box_Any_AVX2,
|
||||
ScaleRowDown2Box_AVX2,
|
||||
ScaleRowDown2Box_C,
|
||||
2,
|
||||
1,
|
||||
31)
|
||||
|
||||
SDODD(ScaleRowDown2Box_Odd_AVX2,
|
||||
ScaleRowDown2Box_AVX2,
|
||||
ScaleRowDown2Box_Odd_C,
|
||||
2,
|
||||
1,
|
||||
31)
|
||||
|
||||
#endif
|
||||
#ifdef HAS_SCALEROWDOWN4_SSSE3
|
||||
|
||||
SDANY(ScaleRowDown4_Any_SSSE3, ScaleRowDown4_SSSE3, ScaleRowDown4_C, 4, 1, 7)
|
||||
|
||||
SDANY(ScaleRowDown4Box_Any_SSSE3,
|
||||
ScaleRowDown4Box_SSSE3,
|
||||
ScaleRowDown4Box_C,
|
||||
4,
|
||||
1,
|
||||
7)
|
||||
|
||||
#endif
|
||||
#ifdef HAS_SCALEROWDOWN4_AVX2
|
||||
|
||||
SDANY(ScaleRowDown4_Any_AVX2, ScaleRowDown4_AVX2, ScaleRowDown4_C, 4, 1, 15)
|
||||
|
||||
SDANY(ScaleRowDown4Box_Any_AVX2,
|
||||
ScaleRowDown4Box_AVX2,
|
||||
ScaleRowDown4Box_C,
|
||||
4,
|
||||
1,
|
||||
15)
|
||||
|
||||
#endif
|
||||
#ifdef HAS_SCALEROWDOWN34_SSSE3
|
||||
|
||||
SDANY(ScaleRowDown34_Any_SSSE3,
|
||||
ScaleRowDown34_SSSE3,
|
||||
ScaleRowDown34_C,
|
||||
4 / 3,
|
||||
1,
|
||||
23)
|
||||
|
||||
SDANY(ScaleRowDown34_0_Box_Any_SSSE3,
|
||||
ScaleRowDown34_0_Box_SSSE3,
|
||||
ScaleRowDown34_0_Box_C,
|
||||
4 / 3,
|
||||
1,
|
||||
23)
|
||||
|
||||
SDANY(ScaleRowDown34_1_Box_Any_SSSE3,
|
||||
ScaleRowDown34_1_Box_SSSE3,
|
||||
ScaleRowDown34_1_Box_C,
|
||||
4 / 3,
|
||||
1,
|
||||
23)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SCALEROWDOWN38_SSSE3
|
||||
|
||||
SDANY(ScaleRowDown38_Any_SSSE3,
|
||||
ScaleRowDown38_SSSE3,
|
||||
ScaleRowDown38_C,
|
||||
8 / 3,
|
||||
1,
|
||||
11)
|
||||
|
||||
SDANY(ScaleRowDown38_3_Box_Any_SSSE3,
|
||||
ScaleRowDown38_3_Box_SSSE3,
|
||||
ScaleRowDown38_3_Box_C,
|
||||
8 / 3,
|
||||
1,
|
||||
5)
|
||||
|
||||
SDANY(ScaleRowDown38_2_Box_Any_SSSE3,
|
||||
ScaleRowDown38_2_Box_SSSE3,
|
||||
ScaleRowDown38_2_Box_C,
|
||||
8 / 3,
|
||||
1,
|
||||
5)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#undef SDANY
|
||||
|
||||
// Scale down by even scale factor.
|
||||
#define SDAANY(NAMEANY, SCALEROWDOWN_SIMD, SCALEROWDOWN_C, BPP, MASK) \
|
||||
void NAMEANY(const uint8_t* src_ptr, ptrdiff_t src_stride, int src_stepx, \
|
||||
uint8_t* dst_ptr, int dst_width) { \
|
||||
int r = dst_width & MASK; \
|
||||
int n = dst_width & ~MASK; \
|
||||
if (n > 0) { \
|
||||
SCALEROWDOWN_SIMD(src_ptr, src_stride, src_stepx, dst_ptr, n); \
|
||||
} \
|
||||
SCALEROWDOWN_C(src_ptr + (n * src_stepx) * BPP, src_stride, src_stepx, \
|
||||
dst_ptr + n * BPP, r); \
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef SASIMDONLY
|
||||
// This also works and uses memcpy and SIMD instead of C, but is slower on ARM
|
||||
|
||||
// Add rows box filter scale down. Using macro from row_any
|
||||
#define SAROW(NAMEANY, ANY_SIMD, SBPP, BPP, MASK) \
|
||||
void NAMEANY(const uint8_t* src_ptr, uint16_t* dst_ptr, int width) { \
|
||||
SIMD_ALIGNED(uint16_t dst_temp[32]); \
|
||||
SIMD_ALIGNED(uint8_t src_temp[32]); \
|
||||
memset(dst_temp, 0, 32 * 2); /* for msan */ \
|
||||
int r = width & MASK; \
|
||||
int n = width & ~MASK; \
|
||||
if (n > 0) { \
|
||||
ANY_SIMD(src_ptr, dst_ptr, n); \
|
||||
} \
|
||||
memcpy(src_temp, src_ptr + n * SBPP, r * SBPP); \
|
||||
memcpy(dst_temp, dst_ptr + n * BPP, r * BPP); \
|
||||
ANY_SIMD(src_temp, dst_temp, MASK + 1); \
|
||||
memcpy(dst_ptr + n * BPP, dst_temp, r * BPP); \
|
||||
}
|
||||
|
||||
#ifdef HAS_SCALEADDROW_SSE2
|
||||
SAROW(ScaleAddRow_Any_SSE2, ScaleAddRow_SSE2, 1, 2, 15)
|
||||
#endif
|
||||
#ifdef HAS_SCALEADDROW_AVX2
|
||||
SAROW(ScaleAddRow_Any_AVX2, ScaleAddRow_AVX2, 1, 2, 31)
|
||||
#endif
|
||||
#undef SAANY
|
||||
|
||||
#else
|
||||
|
||||
// Add rows box filter scale down.
|
||||
#define SAANY(NAMEANY, SCALEADDROW_SIMD, SCALEADDROW_C, MASK) \
|
||||
void NAMEANY(const uint8_t* src_ptr, uint16_t* dst_ptr, int src_width) { \
|
||||
int n = src_width & ~MASK; \
|
||||
if (n > 0) { \
|
||||
SCALEADDROW_SIMD(src_ptr, dst_ptr, n); \
|
||||
} \
|
||||
SCALEADDROW_C(src_ptr + n, dst_ptr + n, src_width & MASK); \
|
||||
}
|
||||
|
||||
#ifdef HAS_SCALEADDROW_SSE2
|
||||
|
||||
SAANY(ScaleAddRow_Any_SSE2, ScaleAddRow_SSE2, ScaleAddRow_C, 15)
|
||||
|
||||
#endif
|
||||
#ifdef HAS_SCALEADDROW_AVX2
|
||||
|
||||
SAANY(ScaleAddRow_Any_AVX2, ScaleAddRow_AVX2, ScaleAddRow_C, 31)
|
||||
|
||||
#endif
|
||||
#undef SAANY
|
||||
|
||||
#endif // SASIMDONLY
|
||||
|
||||
// Scale up horizontally 2 times using linear filter.
|
||||
#define SUH2LANY(NAME, SIMD, C, MASK, PTYPE) \
|
||||
void NAME(const PTYPE* src_ptr, PTYPE* dst_ptr, int dst_width) { \
|
||||
int work_width = (dst_width - 1) & ~1; \
|
||||
int r = work_width & MASK; \
|
||||
int n = work_width & ~MASK; \
|
||||
dst_ptr[0] = src_ptr[0]; \
|
||||
if (work_width > 0) { \
|
||||
if (n != 0) { \
|
||||
SIMD(src_ptr, dst_ptr + 1, n); \
|
||||
} \
|
||||
C(src_ptr + (n / 2), dst_ptr + n + 1, r); \
|
||||
} \
|
||||
dst_ptr[dst_width - 1] = src_ptr[(dst_width - 1) / 2]; \
|
||||
}
|
||||
|
||||
// Even the C versions need to be wrapped, because boundary pixels have to
|
||||
// be handled differently
|
||||
|
||||
SUH2LANY(ScaleRowUp2_Linear_Any_C,
|
||||
ScaleRowUp2_Linear_C,
|
||||
ScaleRowUp2_Linear_C,
|
||||
0,
|
||||
uint8_t)
|
||||
|
||||
SUH2LANY(ScaleRowUp2_Linear_16_Any_C,
|
||||
ScaleRowUp2_Linear_16_C,
|
||||
ScaleRowUp2_Linear_16_C,
|
||||
0,
|
||||
uint16_t)
|
||||
|
||||
#ifdef HAS_SCALEROWUP2_LINEAR_SSE2
|
||||
|
||||
SUH2LANY(ScaleRowUp2_Linear_Any_SSE2,
|
||||
ScaleRowUp2_Linear_SSE2,
|
||||
ScaleRowUp2_Linear_C,
|
||||
15,
|
||||
uint8_t)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SCALEROWUP2_LINEAR_SSSE3
|
||||
|
||||
SUH2LANY(ScaleRowUp2_Linear_Any_SSSE3,
|
||||
ScaleRowUp2_Linear_SSSE3,
|
||||
ScaleRowUp2_Linear_C,
|
||||
15,
|
||||
uint8_t)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SCALEROWUP2_LINEAR_12_SSSE3
|
||||
|
||||
SUH2LANY(ScaleRowUp2_Linear_12_Any_SSSE3,
|
||||
ScaleRowUp2_Linear_12_SSSE3,
|
||||
ScaleRowUp2_Linear_16_C,
|
||||
15,
|
||||
uint16_t)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SCALEROWUP2_LINEAR_16_SSE2
|
||||
|
||||
SUH2LANY(ScaleRowUp2_Linear_16_Any_SSE2,
|
||||
ScaleRowUp2_Linear_16_SSE2,
|
||||
ScaleRowUp2_Linear_16_C,
|
||||
7,
|
||||
uint16_t)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SCALEROWUP2_LINEAR_AVX2
|
||||
|
||||
SUH2LANY(ScaleRowUp2_Linear_Any_AVX2,
|
||||
ScaleRowUp2_Linear_AVX2,
|
||||
ScaleRowUp2_Linear_C,
|
||||
31,
|
||||
uint8_t)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SCALEROWUP2_LINEAR_12_AVX2
|
||||
|
||||
SUH2LANY(ScaleRowUp2_Linear_12_Any_AVX2,
|
||||
ScaleRowUp2_Linear_12_AVX2,
|
||||
ScaleRowUp2_Linear_16_C,
|
||||
31,
|
||||
uint16_t)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SCALEROWUP2_LINEAR_16_AVX2
|
||||
|
||||
SUH2LANY(ScaleRowUp2_Linear_16_Any_AVX2,
|
||||
ScaleRowUp2_Linear_16_AVX2,
|
||||
ScaleRowUp2_Linear_16_C,
|
||||
15,
|
||||
uint16_t)
|
||||
|
||||
#endif
|
||||
#undef SUH2LANY
|
||||
|
||||
// Scale up 2 times using bilinear filter.
|
||||
// This function produces 2 rows at a time.
|
||||
#define SU2BLANY(NAME, SIMD, C, MASK, PTYPE) \
|
||||
void NAME(const PTYPE* src_ptr, ptrdiff_t src_stride, PTYPE* dst_ptr, \
|
||||
ptrdiff_t dst_stride, int dst_width) { \
|
||||
int work_width = (dst_width - 1) & ~1; \
|
||||
int r = work_width & MASK; \
|
||||
int n = work_width & ~MASK; \
|
||||
const PTYPE* sa = src_ptr; \
|
||||
const PTYPE* sb = src_ptr + src_stride; \
|
||||
PTYPE* da = dst_ptr; \
|
||||
PTYPE* db = dst_ptr + dst_stride; \
|
||||
da[0] = (3 * sa[0] + sb[0] + 2) >> 2; \
|
||||
db[0] = (sa[0] + 3 * sb[0] + 2) >> 2; \
|
||||
if (work_width > 0) { \
|
||||
if (n != 0) { \
|
||||
SIMD(sa, sb - sa, da + 1, db - da, n); \
|
||||
} \
|
||||
C(sa + (n / 2), sb - sa, da + n + 1, db - da, r); \
|
||||
} \
|
||||
da[dst_width - 1] = \
|
||||
(3 * sa[(dst_width - 1) / 2] + sb[(dst_width - 1) / 2] + 2) >> 2; \
|
||||
db[dst_width - 1] = \
|
||||
(sa[(dst_width - 1) / 2] + 3 * sb[(dst_width - 1) / 2] + 2) >> 2; \
|
||||
}
|
||||
|
||||
SU2BLANY(ScaleRowUp2_Bilinear_Any_C,
|
||||
ScaleRowUp2_Bilinear_C,
|
||||
ScaleRowUp2_Bilinear_C,
|
||||
0,
|
||||
uint8_t)
|
||||
|
||||
SU2BLANY(ScaleRowUp2_Bilinear_16_Any_C,
|
||||
ScaleRowUp2_Bilinear_16_C,
|
||||
ScaleRowUp2_Bilinear_16_C,
|
||||
0,
|
||||
uint16_t)
|
||||
|
||||
#ifdef HAS_SCALEROWUP2_BILINEAR_SSE2
|
||||
|
||||
SU2BLANY(ScaleRowUp2_Bilinear_Any_SSE2,
|
||||
ScaleRowUp2_Bilinear_SSE2,
|
||||
ScaleRowUp2_Bilinear_C,
|
||||
15,
|
||||
uint8_t)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SCALEROWUP2_BILINEAR_12_SSSE3
|
||||
|
||||
SU2BLANY(ScaleRowUp2_Bilinear_12_Any_SSSE3,
|
||||
ScaleRowUp2_Bilinear_12_SSSE3,
|
||||
ScaleRowUp2_Bilinear_16_C,
|
||||
15,
|
||||
uint16_t)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SCALEROWUP2_BILINEAR_16_SSE2
|
||||
|
||||
SU2BLANY(ScaleRowUp2_Bilinear_16_Any_SSE2,
|
||||
ScaleRowUp2_Bilinear_16_SSE2,
|
||||
ScaleRowUp2_Bilinear_16_C,
|
||||
7,
|
||||
uint16_t)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SCALEROWUP2_BILINEAR_SSSE3
|
||||
|
||||
SU2BLANY(ScaleRowUp2_Bilinear_Any_SSSE3,
|
||||
ScaleRowUp2_Bilinear_SSSE3,
|
||||
ScaleRowUp2_Bilinear_C,
|
||||
15,
|
||||
uint8_t)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SCALEROWUP2_BILINEAR_AVX2
|
||||
|
||||
SU2BLANY(ScaleRowUp2_Bilinear_Any_AVX2,
|
||||
ScaleRowUp2_Bilinear_AVX2,
|
||||
ScaleRowUp2_Bilinear_C,
|
||||
31,
|
||||
uint8_t)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SCALEROWUP2_BILINEAR_12_AVX2
|
||||
|
||||
SU2BLANY(ScaleRowUp2_Bilinear_12_Any_AVX2,
|
||||
ScaleRowUp2_Bilinear_12_AVX2,
|
||||
ScaleRowUp2_Bilinear_16_C,
|
||||
15,
|
||||
uint16_t)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SCALEROWUP2_BILINEAR_16_AVX2
|
||||
|
||||
SU2BLANY(ScaleRowUp2_Bilinear_16_Any_AVX2,
|
||||
ScaleRowUp2_Bilinear_16_AVX2,
|
||||
ScaleRowUp2_Bilinear_16_C,
|
||||
15,
|
||||
uint16_t)
|
||||
|
||||
#endif
|
||||
|
||||
#undef SU2BLANY
|
||||
|
||||
// Scale bi-planar plane up horizontally 2 times using linear filter.
|
||||
#define SBUH2LANY(NAME, SIMD, C, MASK, PTYPE) \
|
||||
void NAME(const PTYPE* src_ptr, PTYPE* dst_ptr, int dst_width) { \
|
||||
int work_width = (dst_width - 1) & ~1; \
|
||||
int r = work_width & MASK; \
|
||||
int n = work_width & ~MASK; \
|
||||
dst_ptr[0] = src_ptr[0]; \
|
||||
dst_ptr[1] = src_ptr[1]; \
|
||||
if (work_width > 0) { \
|
||||
if (n != 0) { \
|
||||
SIMD(src_ptr, dst_ptr + 2, n); \
|
||||
} \
|
||||
C(src_ptr + n, dst_ptr + 2 * n + 2, r); \
|
||||
} \
|
||||
dst_ptr[2 * dst_width - 2] = src_ptr[((dst_width + 1) & ~1) - 2]; \
|
||||
dst_ptr[2 * dst_width - 1] = src_ptr[((dst_width + 1) & ~1) - 1]; \
|
||||
}
|
||||
|
||||
SBUH2LANY(ScaleUVRowUp2_Linear_Any_C,
|
||||
ScaleUVRowUp2_Linear_C,
|
||||
ScaleUVRowUp2_Linear_C,
|
||||
0,
|
||||
uint8_t)
|
||||
|
||||
SBUH2LANY(ScaleUVRowUp2_Linear_16_Any_C,
|
||||
ScaleUVRowUp2_Linear_16_C,
|
||||
ScaleUVRowUp2_Linear_16_C,
|
||||
0,
|
||||
uint16_t)
|
||||
|
||||
#ifdef HAS_SCALEUVROWUP2_LINEAR_SSSE3
|
||||
|
||||
SBUH2LANY(ScaleUVRowUp2_Linear_Any_SSSE3,
|
||||
ScaleUVRowUp2_Linear_SSSE3,
|
||||
ScaleUVRowUp2_Linear_C,
|
||||
7,
|
||||
uint8_t)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SCALEUVROWUP2_LINEAR_AVX2
|
||||
|
||||
SBUH2LANY(ScaleUVRowUp2_Linear_Any_AVX2,
|
||||
ScaleUVRowUp2_Linear_AVX2,
|
||||
ScaleUVRowUp2_Linear_C,
|
||||
15,
|
||||
uint8_t)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SCALEUVROWUP2_LINEAR_16_SSE41
|
||||
|
||||
SBUH2LANY(ScaleUVRowUp2_Linear_16_Any_SSE41,
|
||||
ScaleUVRowUp2_Linear_16_SSE41,
|
||||
ScaleUVRowUp2_Linear_16_C,
|
||||
3,
|
||||
uint16_t)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SCALEUVROWUP2_LINEAR_16_AVX2
|
||||
|
||||
SBUH2LANY(ScaleUVRowUp2_Linear_16_Any_AVX2,
|
||||
ScaleUVRowUp2_Linear_16_AVX2,
|
||||
ScaleUVRowUp2_Linear_16_C,
|
||||
7,
|
||||
uint16_t)
|
||||
|
||||
#endif
|
||||
|
||||
#undef SBUH2LANY
|
||||
|
||||
// Scale bi-planar plane up 2 times using bilinear filter.
|
||||
// This function produces 2 rows at a time.
|
||||
#define SBU2BLANY(NAME, SIMD, C, MASK, PTYPE) \
|
||||
void NAME(const PTYPE* src_ptr, ptrdiff_t src_stride, PTYPE* dst_ptr, \
|
||||
ptrdiff_t dst_stride, int dst_width) { \
|
||||
int work_width = (dst_width - 1) & ~1; \
|
||||
int r = work_width & MASK; \
|
||||
int n = work_width & ~MASK; \
|
||||
const PTYPE* sa = src_ptr; \
|
||||
const PTYPE* sb = src_ptr + src_stride; \
|
||||
PTYPE* da = dst_ptr; \
|
||||
PTYPE* db = dst_ptr + dst_stride; \
|
||||
da[0] = (3 * sa[0] + sb[0] + 2) >> 2; \
|
||||
db[0] = (sa[0] + 3 * sb[0] + 2) >> 2; \
|
||||
da[1] = (3 * sa[1] + sb[1] + 2) >> 2; \
|
||||
db[1] = (sa[1] + 3 * sb[1] + 2) >> 2; \
|
||||
if (work_width > 0) { \
|
||||
if (n != 0) { \
|
||||
SIMD(sa, sb - sa, da + 2, db - da, n); \
|
||||
} \
|
||||
C(sa + n, sb - sa, da + 2 * n + 2, db - da, r); \
|
||||
} \
|
||||
da[2 * dst_width - 2] = (3 * sa[((dst_width + 1) & ~1) - 2] + \
|
||||
sb[((dst_width + 1) & ~1) - 2] + 2) >> \
|
||||
2; \
|
||||
db[2 * dst_width - 2] = (sa[((dst_width + 1) & ~1) - 2] + \
|
||||
3 * sb[((dst_width + 1) & ~1) - 2] + 2) >> \
|
||||
2; \
|
||||
da[2 * dst_width - 1] = (3 * sa[((dst_width + 1) & ~1) - 1] + \
|
||||
sb[((dst_width + 1) & ~1) - 1] + 2) >> \
|
||||
2; \
|
||||
db[2 * dst_width - 1] = (sa[((dst_width + 1) & ~1) - 1] + \
|
||||
3 * sb[((dst_width + 1) & ~1) - 1] + 2) >> \
|
||||
2; \
|
||||
}
|
||||
|
||||
SBU2BLANY(ScaleUVRowUp2_Bilinear_Any_C,
|
||||
ScaleUVRowUp2_Bilinear_C,
|
||||
ScaleUVRowUp2_Bilinear_C,
|
||||
0,
|
||||
uint8_t)
|
||||
|
||||
SBU2BLANY(ScaleUVRowUp2_Bilinear_16_Any_C,
|
||||
ScaleUVRowUp2_Bilinear_16_C,
|
||||
ScaleUVRowUp2_Bilinear_16_C,
|
||||
0,
|
||||
uint16_t)
|
||||
|
||||
#ifdef HAS_SCALEUVROWUP2_BILINEAR_SSSE3
|
||||
|
||||
SBU2BLANY(ScaleUVRowUp2_Bilinear_Any_SSSE3,
|
||||
ScaleUVRowUp2_Bilinear_SSSE3,
|
||||
ScaleUVRowUp2_Bilinear_C,
|
||||
7,
|
||||
uint8_t)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SCALEUVROWUP2_BILINEAR_AVX2
|
||||
|
||||
SBU2BLANY(ScaleUVRowUp2_Bilinear_Any_AVX2,
|
||||
ScaleUVRowUp2_Bilinear_AVX2,
|
||||
ScaleUVRowUp2_Bilinear_C,
|
||||
15,
|
||||
uint8_t)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SCALEUVROWUP2_BILINEAR_16_SSE41
|
||||
|
||||
SBU2BLANY(ScaleUVRowUp2_Bilinear_16_Any_SSE41,
|
||||
ScaleUVRowUp2_Bilinear_16_SSE41,
|
||||
ScaleUVRowUp2_Bilinear_16_C,
|
||||
7,
|
||||
uint16_t)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SCALEUVROWUP2_BILINEAR_16_AVX2
|
||||
|
||||
SBU2BLANY(ScaleUVRowUp2_Bilinear_16_Any_AVX2,
|
||||
ScaleUVRowUp2_Bilinear_16_AVX2,
|
||||
ScaleUVRowUp2_Bilinear_16_C,
|
||||
7,
|
||||
uint16_t)
|
||||
|
||||
#endif
|
||||
|
||||
#undef SBU2BLANY
|
||||
930
pkg/encoder/yuv/libyuv/scale_common.c
Normal file
930
pkg/encoder/yuv/libyuv/scale_common.c
Normal file
|
|
@ -0,0 +1,930 @@
|
|||
/*
|
||||
* Copyright 2013 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "scale.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "cpu_id.h"
|
||||
#include "row.h"
|
||||
#include "scale_row.h"
|
||||
|
||||
#define STATIC_CAST(type, expr) (type)(expr)
|
||||
|
||||
// TODO(fbarchard): make clamp255 preserve negative values.
|
||||
static __inline int32_t clamp255(int32_t v) {
|
||||
return (-(v >= 255) | v) & 255;
|
||||
}
|
||||
|
||||
// Use scale to convert lsb formats to msb, depending how many bits there are:
|
||||
// 32768 = 9 bits
|
||||
// 16384 = 10 bits
|
||||
// 4096 = 12 bits
|
||||
// 256 = 16 bits
|
||||
// TODO(fbarchard): change scale to bits
|
||||
#define C16TO8(v, scale) clamp255(((v) * (scale)) >> 16)
|
||||
|
||||
static __inline int Abs(int v) {
|
||||
return v >= 0 ? v : -v;
|
||||
}
|
||||
|
||||
// CPU agnostic row functions
|
||||
void ScaleRowDown2_C(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_width) {
|
||||
int x;
|
||||
(void) src_stride;
|
||||
for (x = 0; x < dst_width - 1; x += 2) {
|
||||
dst[0] = src_ptr[1];
|
||||
dst[1] = src_ptr[3];
|
||||
dst += 2;
|
||||
src_ptr += 4;
|
||||
}
|
||||
if (dst_width & 1) {
|
||||
dst[0] = src_ptr[1];
|
||||
}
|
||||
}
|
||||
|
||||
void ScaleRowDown2Linear_C(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_width) {
|
||||
const uint8_t *s = src_ptr;
|
||||
int x;
|
||||
(void) src_stride;
|
||||
for (x = 0; x < dst_width - 1; x += 2) {
|
||||
dst[0] = (s[0] + s[1] + 1) >> 1;
|
||||
dst[1] = (s[2] + s[3] + 1) >> 1;
|
||||
dst += 2;
|
||||
s += 4;
|
||||
}
|
||||
if (dst_width & 1) {
|
||||
dst[0] = (s[0] + s[1] + 1) >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
void ScaleRowDown2Box_C(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_width) {
|
||||
const uint8_t *s = src_ptr;
|
||||
const uint8_t *t = src_ptr + src_stride;
|
||||
int x;
|
||||
for (x = 0; x < dst_width - 1; x += 2) {
|
||||
dst[0] = (s[0] + s[1] + t[0] + t[1] + 2) >> 2;
|
||||
dst[1] = (s[2] + s[3] + t[2] + t[3] + 2) >> 2;
|
||||
dst += 2;
|
||||
s += 4;
|
||||
t += 4;
|
||||
}
|
||||
if (dst_width & 1) {
|
||||
dst[0] = (s[0] + s[1] + t[0] + t[1] + 2) >> 2;
|
||||
}
|
||||
}
|
||||
|
||||
void ScaleRowDown2Box_Odd_C(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_width) {
|
||||
const uint8_t *s = src_ptr;
|
||||
const uint8_t *t = src_ptr + src_stride;
|
||||
int x;
|
||||
dst_width -= 1;
|
||||
for (x = 0; x < dst_width - 1; x += 2) {
|
||||
dst[0] = (s[0] + s[1] + t[0] + t[1] + 2) >> 2;
|
||||
dst[1] = (s[2] + s[3] + t[2] + t[3] + 2) >> 2;
|
||||
dst += 2;
|
||||
s += 4;
|
||||
t += 4;
|
||||
}
|
||||
if (dst_width & 1) {
|
||||
dst[0] = (s[0] + s[1] + t[0] + t[1] + 2) >> 2;
|
||||
dst += 1;
|
||||
s += 2;
|
||||
t += 2;
|
||||
}
|
||||
dst[0] = (s[0] + t[0] + 1) >> 1;
|
||||
}
|
||||
|
||||
void ScaleRowDown4_C(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_width) {
|
||||
int x;
|
||||
(void) src_stride;
|
||||
for (x = 0; x < dst_width - 1; x += 2) {
|
||||
dst[0] = src_ptr[2];
|
||||
dst[1] = src_ptr[6];
|
||||
dst += 2;
|
||||
src_ptr += 8;
|
||||
}
|
||||
if (dst_width & 1) {
|
||||
dst[0] = src_ptr[2];
|
||||
}
|
||||
}
|
||||
|
||||
void ScaleRowDown4Box_C(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_width) {
|
||||
intptr_t stride = src_stride;
|
||||
int x;
|
||||
for (x = 0; x < dst_width - 1; x += 2) {
|
||||
dst[0] = (src_ptr[0] + src_ptr[1] + src_ptr[2] + src_ptr[3] +
|
||||
src_ptr[stride + 0] + src_ptr[stride + 1] + src_ptr[stride + 2] +
|
||||
src_ptr[stride + 3] + src_ptr[stride * 2 + 0] +
|
||||
src_ptr[stride * 2 + 1] + src_ptr[stride * 2 + 2] +
|
||||
src_ptr[stride * 2 + 3] + src_ptr[stride * 3 + 0] +
|
||||
src_ptr[stride * 3 + 1] + src_ptr[stride * 3 + 2] +
|
||||
src_ptr[stride * 3 + 3] + 8) >>
|
||||
4;
|
||||
dst[1] = (src_ptr[4] + src_ptr[5] + src_ptr[6] + src_ptr[7] +
|
||||
src_ptr[stride + 4] + src_ptr[stride + 5] + src_ptr[stride + 6] +
|
||||
src_ptr[stride + 7] + src_ptr[stride * 2 + 4] +
|
||||
src_ptr[stride * 2 + 5] + src_ptr[stride * 2 + 6] +
|
||||
src_ptr[stride * 2 + 7] + src_ptr[stride * 3 + 4] +
|
||||
src_ptr[stride * 3 + 5] + src_ptr[stride * 3 + 6] +
|
||||
src_ptr[stride * 3 + 7] + 8) >>
|
||||
4;
|
||||
dst += 2;
|
||||
src_ptr += 8;
|
||||
}
|
||||
if (dst_width & 1) {
|
||||
dst[0] = (src_ptr[0] + src_ptr[1] + src_ptr[2] + src_ptr[3] +
|
||||
src_ptr[stride + 0] + src_ptr[stride + 1] + src_ptr[stride + 2] +
|
||||
src_ptr[stride + 3] + src_ptr[stride * 2 + 0] +
|
||||
src_ptr[stride * 2 + 1] + src_ptr[stride * 2 + 2] +
|
||||
src_ptr[stride * 2 + 3] + src_ptr[stride * 3 + 0] +
|
||||
src_ptr[stride * 3 + 1] + src_ptr[stride * 3 + 2] +
|
||||
src_ptr[stride * 3 + 3] + 8) >>
|
||||
4;
|
||||
}
|
||||
}
|
||||
|
||||
void ScaleRowDown34_C(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_width) {
|
||||
int x;
|
||||
(void) src_stride;
|
||||
assert((dst_width % 3 == 0) && (dst_width > 0));
|
||||
for (x = 0; x < dst_width; x += 3) {
|
||||
dst[0] = src_ptr[0];
|
||||
dst[1] = src_ptr[1];
|
||||
dst[2] = src_ptr[3];
|
||||
dst += 3;
|
||||
src_ptr += 4;
|
||||
}
|
||||
}
|
||||
|
||||
// Filter rows 0 and 1 together, 3 : 1
|
||||
void ScaleRowDown34_0_Box_C(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *d,
|
||||
int dst_width) {
|
||||
const uint8_t *s = src_ptr;
|
||||
const uint8_t *t = src_ptr + src_stride;
|
||||
int x;
|
||||
assert((dst_width % 3 == 0) && (dst_width > 0));
|
||||
for (x = 0; x < dst_width; x += 3) {
|
||||
uint8_t a0 = (s[0] * 3 + s[1] * 1 + 2) >> 2;
|
||||
uint8_t a1 = (s[1] * 1 + s[2] * 1 + 1) >> 1;
|
||||
uint8_t a2 = (s[2] * 1 + s[3] * 3 + 2) >> 2;
|
||||
uint8_t b0 = (t[0] * 3 + t[1] * 1 + 2) >> 2;
|
||||
uint8_t b1 = (t[1] * 1 + t[2] * 1 + 1) >> 1;
|
||||
uint8_t b2 = (t[2] * 1 + t[3] * 3 + 2) >> 2;
|
||||
d[0] = (a0 * 3 + b0 + 2) >> 2;
|
||||
d[1] = (a1 * 3 + b1 + 2) >> 2;
|
||||
d[2] = (a2 * 3 + b2 + 2) >> 2;
|
||||
d += 3;
|
||||
s += 4;
|
||||
t += 4;
|
||||
}
|
||||
}
|
||||
|
||||
// Filter rows 1 and 2 together, 1 : 1
|
||||
void ScaleRowDown34_1_Box_C(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *d,
|
||||
int dst_width) {
|
||||
const uint8_t *s = src_ptr;
|
||||
const uint8_t *t = src_ptr + src_stride;
|
||||
int x;
|
||||
assert((dst_width % 3 == 0) && (dst_width > 0));
|
||||
for (x = 0; x < dst_width; x += 3) {
|
||||
uint8_t a0 = (s[0] * 3 + s[1] * 1 + 2) >> 2;
|
||||
uint8_t a1 = (s[1] * 1 + s[2] * 1 + 1) >> 1;
|
||||
uint8_t a2 = (s[2] * 1 + s[3] * 3 + 2) >> 2;
|
||||
uint8_t b0 = (t[0] * 3 + t[1] * 1 + 2) >> 2;
|
||||
uint8_t b1 = (t[1] * 1 + t[2] * 1 + 1) >> 1;
|
||||
uint8_t b2 = (t[2] * 1 + t[3] * 3 + 2) >> 2;
|
||||
d[0] = (a0 + b0 + 1) >> 1;
|
||||
d[1] = (a1 + b1 + 1) >> 1;
|
||||
d[2] = (a2 + b2 + 1) >> 1;
|
||||
d += 3;
|
||||
s += 4;
|
||||
t += 4;
|
||||
}
|
||||
}
|
||||
|
||||
// Sample position: (O is src sample position, X is dst sample position)
|
||||
//
|
||||
// v dst_ptr at here v stop at here
|
||||
// X O X X O X X O X X O X X O X
|
||||
// ^ src_ptr at here
|
||||
void ScaleRowUp2_Linear_C(const uint8_t *src_ptr,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width) {
|
||||
int src_width = dst_width >> 1;
|
||||
int x;
|
||||
assert((dst_width % 2 == 0) && (dst_width >= 0));
|
||||
for (x = 0; x < src_width; ++x) {
|
||||
dst_ptr[2 * x + 0] = (src_ptr[x + 0] * 3 + src_ptr[x + 1] * 1 + 2) >> 2;
|
||||
dst_ptr[2 * x + 1] = (src_ptr[x + 0] * 1 + src_ptr[x + 1] * 3 + 2) >> 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Sample position: (O is src sample position, X is dst sample position)
|
||||
//
|
||||
// src_ptr at here
|
||||
// X v X X X X X X X X X
|
||||
// O O O O O
|
||||
// X X X X X X X X X X
|
||||
// ^ dst_ptr at here ^ stop at here
|
||||
// X X X X X X X X X X
|
||||
// O O O O O
|
||||
// X X X X X X X X X X
|
||||
void ScaleRowUp2_Bilinear_C(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width) {
|
||||
const uint8_t *s = src_ptr;
|
||||
const uint8_t *t = src_ptr + src_stride;
|
||||
uint8_t *d = dst_ptr;
|
||||
uint8_t *e = dst_ptr + dst_stride;
|
||||
int src_width = dst_width >> 1;
|
||||
int x;
|
||||
assert((dst_width % 2 == 0) && (dst_width >= 0));
|
||||
for (x = 0; x < src_width; ++x) {
|
||||
d[2 * x + 0] =
|
||||
(s[x + 0] * 9 + s[x + 1] * 3 + t[x + 0] * 3 + t[x + 1] * 1 + 8) >> 4;
|
||||
d[2 * x + 1] =
|
||||
(s[x + 0] * 3 + s[x + 1] * 9 + t[x + 0] * 1 + t[x + 1] * 3 + 8) >> 4;
|
||||
e[2 * x + 0] =
|
||||
(s[x + 0] * 3 + s[x + 1] * 1 + t[x + 0] * 9 + t[x + 1] * 3 + 8) >> 4;
|
||||
e[2 * x + 1] =
|
||||
(s[x + 0] * 1 + s[x + 1] * 3 + t[x + 0] * 3 + t[x + 1] * 9 + 8) >> 4;
|
||||
}
|
||||
}
|
||||
|
||||
// Only suitable for at most 14 bit range.
|
||||
void ScaleRowUp2_Linear_16_C(const uint16_t *src_ptr,
|
||||
uint16_t *dst_ptr,
|
||||
int dst_width) {
|
||||
int src_width = dst_width >> 1;
|
||||
int x;
|
||||
assert((dst_width % 2 == 0) && (dst_width >= 0));
|
||||
for (x = 0; x < src_width; ++x) {
|
||||
dst_ptr[2 * x + 0] = (src_ptr[x + 0] * 3 + src_ptr[x + 1] * 1 + 2) >> 2;
|
||||
dst_ptr[2 * x + 1] = (src_ptr[x + 0] * 1 + src_ptr[x + 1] * 3 + 2) >> 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Only suitable for at most 12bit range.
|
||||
void ScaleRowUp2_Bilinear_16_C(const uint16_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint16_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width) {
|
||||
const uint16_t *s = src_ptr;
|
||||
const uint16_t *t = src_ptr + src_stride;
|
||||
uint16_t *d = dst_ptr;
|
||||
uint16_t *e = dst_ptr + dst_stride;
|
||||
int src_width = dst_width >> 1;
|
||||
int x;
|
||||
assert((dst_width % 2 == 0) && (dst_width >= 0));
|
||||
for (x = 0; x < src_width; ++x) {
|
||||
d[2 * x + 0] =
|
||||
(s[x + 0] * 9 + s[x + 1] * 3 + t[x + 0] * 3 + t[x + 1] * 1 + 8) >> 4;
|
||||
d[2 * x + 1] =
|
||||
(s[x + 0] * 3 + s[x + 1] * 9 + t[x + 0] * 1 + t[x + 1] * 3 + 8) >> 4;
|
||||
e[2 * x + 0] =
|
||||
(s[x + 0] * 3 + s[x + 1] * 1 + t[x + 0] * 9 + t[x + 1] * 3 + 8) >> 4;
|
||||
e[2 * x + 1] =
|
||||
(s[x + 0] * 1 + s[x + 1] * 3 + t[x + 0] * 3 + t[x + 1] * 9 + 8) >> 4;
|
||||
}
|
||||
}
|
||||
|
||||
// (1-f)a + fb can be replaced with a + f(b-a)
|
||||
#if defined(__arm__) || defined(__aarch64__)
|
||||
#define BLENDER(a, b, f) \
|
||||
(uint8_t)((int)(a) + ((((int)((f)) * ((int)(b) - (int)(a))) + 0x8000) >> 16))
|
||||
#else
|
||||
// Intel uses 7 bit math with rounding.
|
||||
#define BLENDER(a, b, f) \
|
||||
(uint8_t)((int)(a) + (((int)((f) >> 9) * ((int)(b) - (int)(a)) + 0x40) >> 7))
|
||||
#endif
|
||||
|
||||
void ScaleFilterCols_C(uint8_t *dst_ptr,
|
||||
const uint8_t *src_ptr,
|
||||
int dst_width,
|
||||
int x,
|
||||
int dx) {
|
||||
int j;
|
||||
for (j = 0; j < dst_width - 1; j += 2) {
|
||||
int xi = x >> 16;
|
||||
int a = src_ptr[xi];
|
||||
int b = src_ptr[xi + 1];
|
||||
dst_ptr[0] = BLENDER(a, b, x & 0xffff);
|
||||
x += dx;
|
||||
xi = x >> 16;
|
||||
a = src_ptr[xi];
|
||||
b = src_ptr[xi + 1];
|
||||
dst_ptr[1] = BLENDER(a, b, x & 0xffff);
|
||||
x += dx;
|
||||
dst_ptr += 2;
|
||||
}
|
||||
if (dst_width & 1) {
|
||||
int xi = x >> 16;
|
||||
int a = src_ptr[xi];
|
||||
int b = src_ptr[xi + 1];
|
||||
dst_ptr[0] = BLENDER(a, b, x & 0xffff);
|
||||
}
|
||||
}
|
||||
|
||||
void ScaleFilterCols64_C(uint8_t *dst_ptr,
|
||||
const uint8_t *src_ptr,
|
||||
int dst_width,
|
||||
int x32,
|
||||
int dx) {
|
||||
int64_t x = (int64_t) (x32);
|
||||
int j;
|
||||
for (j = 0; j < dst_width - 1; j += 2) {
|
||||
int64_t xi = x >> 16;
|
||||
int a = src_ptr[xi];
|
||||
int b = src_ptr[xi + 1];
|
||||
dst_ptr[0] = BLENDER(a, b, x & 0xffff);
|
||||
x += dx;
|
||||
xi = x >> 16;
|
||||
a = src_ptr[xi];
|
||||
b = src_ptr[xi + 1];
|
||||
dst_ptr[1] = BLENDER(a, b, x & 0xffff);
|
||||
x += dx;
|
||||
dst_ptr += 2;
|
||||
}
|
||||
if (dst_width & 1) {
|
||||
int64_t xi = x >> 16;
|
||||
int a = src_ptr[xi];
|
||||
int b = src_ptr[xi + 1];
|
||||
dst_ptr[0] = BLENDER(a, b, x & 0xffff);
|
||||
}
|
||||
}
|
||||
|
||||
#undef BLENDER
|
||||
|
||||
// Same as 8 bit arm blender but return is cast to uint16_t
|
||||
#define BLENDER(a, b, f) \
|
||||
(uint16_t)( \
|
||||
(int)(a) + \
|
||||
(int)((((int64_t)((f)) * ((int64_t)(b) - (int)(a))) + 0x8000) >> 16))
|
||||
#undef BLENDER
|
||||
|
||||
void ScaleRowDown38_C(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_width) {
|
||||
int x;
|
||||
(void) src_stride;
|
||||
assert(dst_width % 3 == 0);
|
||||
for (x = 0; x < dst_width; x += 3) {
|
||||
dst[0] = src_ptr[0];
|
||||
dst[1] = src_ptr[3];
|
||||
dst[2] = src_ptr[6];
|
||||
dst += 3;
|
||||
src_ptr += 8;
|
||||
}
|
||||
}
|
||||
|
||||
// 8x3 -> 3x1
|
||||
void ScaleRowDown38_3_Box_C(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width) {
|
||||
intptr_t stride = src_stride;
|
||||
int i;
|
||||
assert((dst_width % 3 == 0) && (dst_width > 0));
|
||||
for (i = 0; i < dst_width; i += 3) {
|
||||
dst_ptr[0] =
|
||||
(src_ptr[0] + src_ptr[1] + src_ptr[2] + src_ptr[stride + 0] +
|
||||
src_ptr[stride + 1] + src_ptr[stride + 2] + src_ptr[stride * 2 + 0] +
|
||||
src_ptr[stride * 2 + 1] + src_ptr[stride * 2 + 2]) *
|
||||
(65536 / 9) >>
|
||||
16;
|
||||
dst_ptr[1] =
|
||||
(src_ptr[3] + src_ptr[4] + src_ptr[5] + src_ptr[stride + 3] +
|
||||
src_ptr[stride + 4] + src_ptr[stride + 5] + src_ptr[stride * 2 + 3] +
|
||||
src_ptr[stride * 2 + 4] + src_ptr[stride * 2 + 5]) *
|
||||
(65536 / 9) >>
|
||||
16;
|
||||
dst_ptr[2] =
|
||||
(src_ptr[6] + src_ptr[7] + src_ptr[stride + 6] + src_ptr[stride + 7] +
|
||||
src_ptr[stride * 2 + 6] + src_ptr[stride * 2 + 7]) *
|
||||
(65536 / 6) >>
|
||||
16;
|
||||
src_ptr += 8;
|
||||
dst_ptr += 3;
|
||||
}
|
||||
}
|
||||
|
||||
// 8x2 -> 3x1
|
||||
void ScaleRowDown38_2_Box_C(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width) {
|
||||
intptr_t stride = src_stride;
|
||||
int i;
|
||||
assert((dst_width % 3 == 0) && (dst_width > 0));
|
||||
for (i = 0; i < dst_width; i += 3) {
|
||||
dst_ptr[0] = (src_ptr[0] + src_ptr[1] + src_ptr[2] + src_ptr[stride + 0] +
|
||||
src_ptr[stride + 1] + src_ptr[stride + 2]) *
|
||||
(65536 / 6) >>
|
||||
16;
|
||||
dst_ptr[1] = (src_ptr[3] + src_ptr[4] + src_ptr[5] + src_ptr[stride + 3] +
|
||||
src_ptr[stride + 4] + src_ptr[stride + 5]) *
|
||||
(65536 / 6) >>
|
||||
16;
|
||||
dst_ptr[2] =
|
||||
(src_ptr[6] + src_ptr[7] + src_ptr[stride + 6] + src_ptr[stride + 7]) *
|
||||
(65536 / 4) >>
|
||||
16;
|
||||
src_ptr += 8;
|
||||
dst_ptr += 3;
|
||||
}
|
||||
}
|
||||
|
||||
void ScaleAddRow_C(const uint8_t *src_ptr, uint16_t *dst_ptr, int src_width) {
|
||||
int x;
|
||||
assert(src_width > 0);
|
||||
for (x = 0; x < src_width - 1; x += 2) {
|
||||
dst_ptr[0] += src_ptr[0];
|
||||
dst_ptr[1] += src_ptr[1];
|
||||
src_ptr += 2;
|
||||
dst_ptr += 2;
|
||||
}
|
||||
if (src_width & 1) {
|
||||
dst_ptr[0] += src_ptr[0];
|
||||
}
|
||||
}
|
||||
|
||||
// UV scale row functions
|
||||
// same as ARGB but 2 channels
|
||||
|
||||
void ScaleUVRowDown2_C(const uint8_t *src_uv,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_uv,
|
||||
int dst_width) {
|
||||
int x;
|
||||
(void) src_stride;
|
||||
for (x = 0; x < dst_width; ++x) {
|
||||
dst_uv[0] = src_uv[2]; // Store the 2nd UV
|
||||
dst_uv[1] = src_uv[3];
|
||||
src_uv += 4;
|
||||
dst_uv += 2;
|
||||
}
|
||||
}
|
||||
|
||||
void ScaleUVRowDown2Linear_C(const uint8_t *src_uv,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_uv,
|
||||
int dst_width) {
|
||||
int x;
|
||||
(void) src_stride;
|
||||
for (x = 0; x < dst_width; ++x) {
|
||||
dst_uv[0] = (src_uv[0] + src_uv[2] + 1) >> 1;
|
||||
dst_uv[1] = (src_uv[1] + src_uv[3] + 1) >> 1;
|
||||
src_uv += 4;
|
||||
dst_uv += 2;
|
||||
}
|
||||
}
|
||||
|
||||
void ScaleUVRowDown2Box_C(const uint8_t *src_uv,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_uv,
|
||||
int dst_width) {
|
||||
int x;
|
||||
for (x = 0; x < dst_width; ++x) {
|
||||
dst_uv[0] = (src_uv[0] + src_uv[2] + src_uv[src_stride] +
|
||||
src_uv[src_stride + 2] + 2) >>
|
||||
2;
|
||||
dst_uv[1] = (src_uv[1] + src_uv[3] + src_uv[src_stride + 1] +
|
||||
src_uv[src_stride + 3] + 2) >>
|
||||
2;
|
||||
src_uv += 4;
|
||||
dst_uv += 2;
|
||||
}
|
||||
}
|
||||
|
||||
void ScaleUVRowDownEven_C(const uint8_t *src_uv,
|
||||
ptrdiff_t src_stride,
|
||||
int src_stepx,
|
||||
uint8_t *dst_uv,
|
||||
int dst_width) {
|
||||
const uint16_t *src = (const uint16_t *) (src_uv);
|
||||
uint16_t *dst = (uint16_t *) (dst_uv);
|
||||
(void) src_stride;
|
||||
int x;
|
||||
for (x = 0; x < dst_width - 1; x += 2) {
|
||||
dst[0] = src[0];
|
||||
dst[1] = src[src_stepx];
|
||||
src += src_stepx * 2;
|
||||
dst += 2;
|
||||
}
|
||||
if (dst_width & 1) {
|
||||
dst[0] = src[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Scales a single row of pixels using point sampling.
|
||||
void ScaleCols_C(uint8_t *dst_ptr,
|
||||
const uint8_t *src_ptr,
|
||||
int dst_width,
|
||||
int x,
|
||||
int dx) {
|
||||
int j;
|
||||
for (j = 0; j < dst_width - 1; j += 2) {
|
||||
dst_ptr[0] = src_ptr[x >> 16];
|
||||
x += dx;
|
||||
dst_ptr[1] = src_ptr[x >> 16];
|
||||
x += dx;
|
||||
dst_ptr += 2;
|
||||
}
|
||||
if (dst_width & 1) {
|
||||
dst_ptr[0] = src_ptr[x >> 16];
|
||||
}
|
||||
}
|
||||
|
||||
// Scales a single row of pixels up by 2x using point sampling.
|
||||
void ScaleColsUp2_C(uint8_t *dst_ptr,
|
||||
const uint8_t *src_ptr,
|
||||
int dst_width,
|
||||
int x,
|
||||
int dx) {
|
||||
int j;
|
||||
(void) x;
|
||||
(void) dx;
|
||||
for (j = 0; j < dst_width - 1; j += 2) {
|
||||
dst_ptr[1] = dst_ptr[0] = src_ptr[0];
|
||||
src_ptr += 1;
|
||||
dst_ptr += 2;
|
||||
}
|
||||
if (dst_width & 1) {
|
||||
dst_ptr[0] = src_ptr[0];
|
||||
}
|
||||
}
|
||||
|
||||
void ScaleUVRowUp2_Linear_C(const uint8_t *src_ptr,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width) {
|
||||
int src_width = dst_width >> 1;
|
||||
int x;
|
||||
assert((dst_width % 2 == 0) && (dst_width >= 0));
|
||||
for (x = 0; x < src_width; ++x) {
|
||||
dst_ptr[4 * x + 0] =
|
||||
(src_ptr[2 * x + 0] * 3 + src_ptr[2 * x + 2] * 1 + 2) >> 2;
|
||||
dst_ptr[4 * x + 1] =
|
||||
(src_ptr[2 * x + 1] * 3 + src_ptr[2 * x + 3] * 1 + 2) >> 2;
|
||||
dst_ptr[4 * x + 2] =
|
||||
(src_ptr[2 * x + 0] * 1 + src_ptr[2 * x + 2] * 3 + 2) >> 2;
|
||||
dst_ptr[4 * x + 3] =
|
||||
(src_ptr[2 * x + 1] * 1 + src_ptr[2 * x + 3] * 3 + 2) >> 2;
|
||||
}
|
||||
}
|
||||
|
||||
void ScaleUVRowUp2_Bilinear_C(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width) {
|
||||
const uint8_t *s = src_ptr;
|
||||
const uint8_t *t = src_ptr + src_stride;
|
||||
uint8_t *d = dst_ptr;
|
||||
uint8_t *e = dst_ptr + dst_stride;
|
||||
int src_width = dst_width >> 1;
|
||||
int x;
|
||||
assert((dst_width % 2 == 0) && (dst_width >= 0));
|
||||
for (x = 0; x < src_width; ++x) {
|
||||
d[4 * x + 0] = (s[2 * x + 0] * 9 + s[2 * x + 2] * 3 + t[2 * x + 0] * 3 +
|
||||
t[2 * x + 2] * 1 + 8) >>
|
||||
4;
|
||||
d[4 * x + 1] = (s[2 * x + 1] * 9 + s[2 * x + 3] * 3 + t[2 * x + 1] * 3 +
|
||||
t[2 * x + 3] * 1 + 8) >>
|
||||
4;
|
||||
d[4 * x + 2] = (s[2 * x + 0] * 3 + s[2 * x + 2] * 9 + t[2 * x + 0] * 1 +
|
||||
t[2 * x + 2] * 3 + 8) >>
|
||||
4;
|
||||
d[4 * x + 3] = (s[2 * x + 1] * 3 + s[2 * x + 3] * 9 + t[2 * x + 1] * 1 +
|
||||
t[2 * x + 3] * 3 + 8) >>
|
||||
4;
|
||||
e[4 * x + 0] = (s[2 * x + 0] * 3 + s[2 * x + 2] * 1 + t[2 * x + 0] * 9 +
|
||||
t[2 * x + 2] * 3 + 8) >>
|
||||
4;
|
||||
e[4 * x + 1] = (s[2 * x + 1] * 3 + s[2 * x + 3] * 1 + t[2 * x + 1] * 9 +
|
||||
t[2 * x + 3] * 3 + 8) >>
|
||||
4;
|
||||
e[4 * x + 2] = (s[2 * x + 0] * 1 + s[2 * x + 2] * 3 + t[2 * x + 0] * 3 +
|
||||
t[2 * x + 2] * 9 + 8) >>
|
||||
4;
|
||||
e[4 * x + 3] = (s[2 * x + 1] * 1 + s[2 * x + 3] * 3 + t[2 * x + 1] * 3 +
|
||||
t[2 * x + 3] * 9 + 8) >>
|
||||
4;
|
||||
}
|
||||
}
|
||||
|
||||
void ScaleUVRowUp2_Linear_16_C(const uint16_t *src_ptr,
|
||||
uint16_t *dst_ptr,
|
||||
int dst_width) {
|
||||
int src_width = dst_width >> 1;
|
||||
int x;
|
||||
assert((dst_width % 2 == 0) && (dst_width >= 0));
|
||||
for (x = 0; x < src_width; ++x) {
|
||||
dst_ptr[4 * x + 0] =
|
||||
(src_ptr[2 * x + 0] * 3 + src_ptr[2 * x + 2] * 1 + 2) >> 2;
|
||||
dst_ptr[4 * x + 1] =
|
||||
(src_ptr[2 * x + 1] * 3 + src_ptr[2 * x + 3] * 1 + 2) >> 2;
|
||||
dst_ptr[4 * x + 2] =
|
||||
(src_ptr[2 * x + 0] * 1 + src_ptr[2 * x + 2] * 3 + 2) >> 2;
|
||||
dst_ptr[4 * x + 3] =
|
||||
(src_ptr[2 * x + 1] * 1 + src_ptr[2 * x + 3] * 3 + 2) >> 2;
|
||||
}
|
||||
}
|
||||
|
||||
void ScaleUVRowUp2_Bilinear_16_C(const uint16_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint16_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width) {
|
||||
const uint16_t *s = src_ptr;
|
||||
const uint16_t *t = src_ptr + src_stride;
|
||||
uint16_t *d = dst_ptr;
|
||||
uint16_t *e = dst_ptr + dst_stride;
|
||||
int src_width = dst_width >> 1;
|
||||
int x;
|
||||
assert((dst_width % 2 == 0) && (dst_width >= 0));
|
||||
for (x = 0; x < src_width; ++x) {
|
||||
d[4 * x + 0] = (s[2 * x + 0] * 9 + s[2 * x + 2] * 3 + t[2 * x + 0] * 3 +
|
||||
t[2 * x + 2] * 1 + 8) >>
|
||||
4;
|
||||
d[4 * x + 1] = (s[2 * x + 1] * 9 + s[2 * x + 3] * 3 + t[2 * x + 1] * 3 +
|
||||
t[2 * x + 3] * 1 + 8) >>
|
||||
4;
|
||||
d[4 * x + 2] = (s[2 * x + 0] * 3 + s[2 * x + 2] * 9 + t[2 * x + 0] * 1 +
|
||||
t[2 * x + 2] * 3 + 8) >>
|
||||
4;
|
||||
d[4 * x + 3] = (s[2 * x + 1] * 3 + s[2 * x + 3] * 9 + t[2 * x + 1] * 1 +
|
||||
t[2 * x + 3] * 3 + 8) >>
|
||||
4;
|
||||
e[4 * x + 0] = (s[2 * x + 0] * 3 + s[2 * x + 2] * 1 + t[2 * x + 0] * 9 +
|
||||
t[2 * x + 2] * 3 + 8) >>
|
||||
4;
|
||||
e[4 * x + 1] = (s[2 * x + 1] * 3 + s[2 * x + 3] * 1 + t[2 * x + 1] * 9 +
|
||||
t[2 * x + 3] * 3 + 8) >>
|
||||
4;
|
||||
e[4 * x + 2] = (s[2 * x + 0] * 1 + s[2 * x + 2] * 3 + t[2 * x + 0] * 3 +
|
||||
t[2 * x + 2] * 9 + 8) >>
|
||||
4;
|
||||
e[4 * x + 3] = (s[2 * x + 1] * 1 + s[2 * x + 3] * 3 + t[2 * x + 1] * 3 +
|
||||
t[2 * x + 3] * 9 + 8) >>
|
||||
4;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(fbarchard): Replace 0x7f ^ f with 128-f. bug=607.
|
||||
// Mimics SSSE3 blender
|
||||
#define BLENDER1(a, b, f) ((a) * (0x7f ^ f) + (b)*f) >> 7
|
||||
#define BLENDERC(a, b, f, s) \
|
||||
(uint16_t)(BLENDER1(((a) >> s) & 255, ((b) >> s) & 255, f) << s)
|
||||
#define BLENDER(a, b, f) BLENDERC(a, b, f, 8) | BLENDERC(a, b, f, 0)
|
||||
|
||||
void ScaleUVFilterCols_C(uint8_t *dst_uv,
|
||||
const uint8_t *src_uv,
|
||||
int dst_width,
|
||||
int x,
|
||||
int dx) {
|
||||
const uint16_t *src = (const uint16_t *) (src_uv);
|
||||
uint16_t *dst = (uint16_t *) (dst_uv);
|
||||
int j;
|
||||
for (j = 0; j < dst_width - 1; j += 2) {
|
||||
int xi = x >> 16;
|
||||
int xf = (x >> 9) & 0x7f;
|
||||
uint16_t a = src[xi];
|
||||
uint16_t b = src[xi + 1];
|
||||
dst[0] = BLENDER(a, b, xf);
|
||||
x += dx;
|
||||
xi = x >> 16;
|
||||
xf = (x >> 9) & 0x7f;
|
||||
a = src[xi];
|
||||
b = src[xi + 1];
|
||||
dst[1] = BLENDER(a, b, xf);
|
||||
x += dx;
|
||||
dst += 2;
|
||||
}
|
||||
if (dst_width & 1) {
|
||||
int xi = x >> 16;
|
||||
int xf = (x >> 9) & 0x7f;
|
||||
uint16_t a = src[xi];
|
||||
uint16_t b = src[xi + 1];
|
||||
dst[0] = BLENDER(a, b, xf);
|
||||
}
|
||||
}
|
||||
|
||||
#undef BLENDER1
|
||||
#undef BLENDERC
|
||||
#undef BLENDER
|
||||
|
||||
// Scale plane vertically with bilinear interpolation.
|
||||
void ScalePlaneVertical(int src_height,
|
||||
int dst_width,
|
||||
int dst_height,
|
||||
int src_stride,
|
||||
int dst_stride,
|
||||
const uint8_t *src_argb,
|
||||
uint8_t *dst_argb,
|
||||
int x,
|
||||
int y,
|
||||
int dy,
|
||||
int bpp, // bytes per pixel. 4 for ARGB.
|
||||
enum FilterMode filtering) {
|
||||
// TODO(fbarchard): Allow higher bpp.
|
||||
int dst_width_bytes = dst_width * bpp;
|
||||
void (*InterpolateRow)(uint8_t *dst_argb, const uint8_t *src_argb,
|
||||
ptrdiff_t src_stride, int dst_width,
|
||||
int source_y_fraction) = InterpolateRow_C;
|
||||
const int max_y = (src_height > 1) ? ((src_height - 1) << 16) - 1 : 0;
|
||||
int j;
|
||||
assert(bpp >= 1 && bpp <= 4);
|
||||
assert(src_height != 0);
|
||||
assert(dst_width > 0);
|
||||
assert(dst_height > 0);
|
||||
src_argb += (x >> 16) * bpp;
|
||||
#if defined(HAS_INTERPOLATEROW_SSSE3)
|
||||
if (TestCpuFlag(kCpuHasSSSE3)) {
|
||||
InterpolateRow = InterpolateRow_Any_SSSE3;
|
||||
if (IS_ALIGNED(dst_width_bytes, 16)) {
|
||||
InterpolateRow = InterpolateRow_SSSE3;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(HAS_INTERPOLATEROW_AVX2)
|
||||
if (TestCpuFlag(kCpuHasAVX2)) {
|
||||
InterpolateRow = InterpolateRow_Any_AVX2;
|
||||
if (IS_ALIGNED(dst_width_bytes, 32)) {
|
||||
InterpolateRow = InterpolateRow_AVX2;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
for (j = 0; j < dst_height; ++j) {
|
||||
int yi;
|
||||
int yf;
|
||||
if (y > max_y) {
|
||||
y = max_y;
|
||||
}
|
||||
yi = y >> 16;
|
||||
yf = filtering ? ((y >> 8) & 255) : 0;
|
||||
InterpolateRow(dst_argb, src_argb + yi * src_stride, src_stride,
|
||||
dst_width_bytes, yf);
|
||||
dst_argb += dst_stride;
|
||||
y += dy;
|
||||
}
|
||||
}
|
||||
|
||||
// Simplify the filtering based on scale factors.
|
||||
enum FilterMode ScaleFilterReduce(int src_width,
|
||||
int src_height,
|
||||
int dst_width,
|
||||
int dst_height,
|
||||
enum FilterMode filtering) {
|
||||
if (src_width < 0) {
|
||||
src_width = -src_width;
|
||||
}
|
||||
if (src_height < 0) {
|
||||
src_height = -src_height;
|
||||
}
|
||||
if (filtering == kFilterBox) {
|
||||
// If scaling either axis to 0.5 or larger, switch from Box to Bilinear.
|
||||
if (dst_width * 2 >= src_width || dst_height * 2 >= src_height) {
|
||||
filtering = kFilterBilinear;
|
||||
}
|
||||
}
|
||||
if (filtering == kFilterBilinear) {
|
||||
if (src_height == 1) {
|
||||
filtering = kFilterLinear;
|
||||
}
|
||||
// TODO(fbarchard): Detect any odd scale factor and reduce to Linear.
|
||||
if (dst_height == src_height || dst_height * 3 == src_height) {
|
||||
filtering = kFilterLinear;
|
||||
}
|
||||
// TODO(fbarchard): Remove 1 pixel wide filter restriction, which is to
|
||||
// avoid reading 2 pixels horizontally that causes memory exception.
|
||||
if (src_width == 1) {
|
||||
filtering = kFilterNone;
|
||||
}
|
||||
}
|
||||
if (filtering == kFilterLinear) {
|
||||
if (src_width == 1) {
|
||||
filtering = kFilterNone;
|
||||
}
|
||||
// TODO(fbarchard): Detect any odd scale factor and reduce to None.
|
||||
if (dst_width == src_width || dst_width * 3 == src_width) {
|
||||
filtering = kFilterNone;
|
||||
}
|
||||
}
|
||||
return filtering;
|
||||
}
|
||||
|
||||
#define CENTERSTART(dx, s) (dx < 0) ? -((-dx >> 1) + s) : ((dx >> 1) + s)
|
||||
|
||||
// Compute slope values for stepping.
|
||||
void ScaleSlope(int src_width,
|
||||
int src_height,
|
||||
int dst_width,
|
||||
int dst_height,
|
||||
enum FilterMode filtering,
|
||||
int *x,
|
||||
int *y,
|
||||
int *dx,
|
||||
int *dy) {
|
||||
assert(x != NULL);
|
||||
assert(y != NULL);
|
||||
assert(dx != NULL);
|
||||
assert(dy != NULL);
|
||||
assert(src_width != 0);
|
||||
assert(src_height != 0);
|
||||
assert(dst_width > 0);
|
||||
assert(dst_height > 0);
|
||||
// Check for 1 pixel and avoid FixedDiv overflow.
|
||||
if (dst_width == 1 && src_width >= 32768) {
|
||||
dst_width = src_width;
|
||||
}
|
||||
if (dst_height == 1 && src_height >= 32768) {
|
||||
dst_height = src_height;
|
||||
}
|
||||
if (filtering == kFilterBox) {
|
||||
// Scale step for point sampling duplicates all pixels equally.
|
||||
*dx = FixedDiv(Abs(src_width), dst_width);
|
||||
*dy = FixedDiv(src_height, dst_height);
|
||||
*x = 0;
|
||||
*y = 0;
|
||||
} else if (filtering == kFilterBilinear) {
|
||||
// Scale step for bilinear sampling renders last pixel once for upsample.
|
||||
if (dst_width <= Abs(src_width)) {
|
||||
*dx = FixedDiv(Abs(src_width), dst_width);
|
||||
*x = CENTERSTART(*dx, -32768); // Subtract 0.5 (32768) to center filter.
|
||||
} else if (src_width > 1 && dst_width > 1) {
|
||||
*dx = FixedDiv1(Abs(src_width), dst_width);
|
||||
*x = 0;
|
||||
}
|
||||
if (dst_height <= src_height) {
|
||||
*dy = FixedDiv(src_height, dst_height);
|
||||
*y = CENTERSTART(*dy, -32768); // Subtract 0.5 (32768) to center filter.
|
||||
} else if (src_height > 1 && dst_height > 1) {
|
||||
*dy = FixedDiv1(src_height, dst_height);
|
||||
*y = 0;
|
||||
}
|
||||
} else if (filtering == kFilterLinear) {
|
||||
// Scale step for bilinear sampling renders last pixel once for upsample.
|
||||
if (dst_width <= Abs(src_width)) {
|
||||
*dx = FixedDiv(Abs(src_width), dst_width);
|
||||
*x = CENTERSTART(*dx, -32768); // Subtract 0.5 (32768) to center filter.
|
||||
} else if (src_width > 1 && dst_width > 1) {
|
||||
*dx = FixedDiv1(Abs(src_width), dst_width);
|
||||
*x = 0;
|
||||
}
|
||||
*dy = FixedDiv(src_height, dst_height);
|
||||
*y = *dy >> 1;
|
||||
} else {
|
||||
// Scale step for point sampling duplicates all pixels equally.
|
||||
*dx = FixedDiv(Abs(src_width), dst_width);
|
||||
*dy = FixedDiv(src_height, dst_height);
|
||||
*x = CENTERSTART(*dx, 0);
|
||||
*y = CENTERSTART(*dy, 0);
|
||||
}
|
||||
// Negative src_width means horizontally mirror.
|
||||
if (src_width < 0) {
|
||||
*x += (dst_width - 1) * *dx;
|
||||
*dx = -*dx;
|
||||
// src_width = -src_width; // Caller must do this.
|
||||
}
|
||||
}
|
||||
|
||||
#undef CENTERSTART
|
||||
2651
pkg/encoder/yuv/libyuv/scale_gcc.c
Normal file
2651
pkg/encoder/yuv/libyuv/scale_gcc.c
Normal file
File diff suppressed because it is too large
Load diff
768
pkg/encoder/yuv/libyuv/scale_row.h
Normal file
768
pkg/encoder/yuv/libyuv/scale_row.h
Normal file
|
|
@ -0,0 +1,768 @@
|
|||
/*
|
||||
* Copyright 2013 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_LIBYUV_SCALE_ROW_H_
|
||||
#define INCLUDE_LIBYUV_SCALE_ROW_H_
|
||||
|
||||
#include "basic_types.h"
|
||||
#include "scale.h"
|
||||
|
||||
#if defined(__pnacl__) || defined(__CLR_VER) || \
|
||||
(defined(__native_client__) && defined(__x86_64__)) || \
|
||||
(defined(__i386__) && !defined(__SSE__) && !defined(__clang__))
|
||||
#define LIBYUV_DISABLE_X86
|
||||
#endif
|
||||
#if defined(__native_client__)
|
||||
#define LIBYUV_DISABLE_NEON
|
||||
#endif
|
||||
// MemorySanitizer does not support assembly code yet. http://crbug.com/344505
|
||||
#if defined(__has_feature)
|
||||
#if __has_feature(memory_sanitizer) && !defined(LIBYUV_DISABLE_NEON)
|
||||
#define LIBYUV_DISABLE_NEON
|
||||
#endif
|
||||
#if __has_feature(memory_sanitizer) && !defined(LIBYUV_DISABLE_X86)
|
||||
#define LIBYUV_DISABLE_X86
|
||||
#endif
|
||||
#endif
|
||||
// GCC >= 4.7.0 required for AVX2.
|
||||
#if defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__))
|
||||
#if (__GNUC__ > 4) || (__GNUC__ == 4 && (__GNUC_MINOR__ >= 7))
|
||||
#define GCC_HAS_AVX2 1
|
||||
#endif // GNUC >= 4.7
|
||||
#endif // __GNUC__
|
||||
|
||||
// The following are available on all x86 platforms:
|
||||
#if !defined(LIBYUV_DISABLE_X86) && \
|
||||
(defined(_M_IX86) || defined(__x86_64__) || defined(__i386__))
|
||||
#define HAS_FIXEDDIV1_X86
|
||||
#define HAS_FIXEDDIV_X86
|
||||
#define HAS_SCALEADDROW_SSE2
|
||||
#define HAS_SCALECOLSUP2_SSE2
|
||||
#define HAS_SCALEFILTERCOLS_SSSE3
|
||||
#define HAS_SCALEROWDOWN2_SSSE3
|
||||
#define HAS_SCALEROWDOWN34_SSSE3
|
||||
#define HAS_SCALEROWDOWN38_SSSE3
|
||||
#define HAS_SCALEROWDOWN4_SSSE3
|
||||
#endif
|
||||
|
||||
// The following are available for gcc/clang x86 platforms:
|
||||
// TODO(fbarchard): Port to Visual C
|
||||
#if !defined(LIBYUV_DISABLE_X86) && (defined(__x86_64__) || defined(__i386__))
|
||||
#define HAS_SCALEUVROWDOWN2BOX_SSSE3
|
||||
#define HAS_SCALEROWUP2_LINEAR_SSE2
|
||||
#define HAS_SCALEROWUP2_LINEAR_SSSE3
|
||||
#define HAS_SCALEROWUP2_BILINEAR_SSE2
|
||||
#define HAS_SCALEROWUP2_BILINEAR_SSSE3
|
||||
#define HAS_SCALEROWUP2_LINEAR_12_SSSE3
|
||||
#define HAS_SCALEROWUP2_BILINEAR_12_SSSE3
|
||||
#define HAS_SCALEROWUP2_LINEAR_16_SSE2
|
||||
#define HAS_SCALEROWUP2_BILINEAR_16_SSE2
|
||||
#define HAS_SCALEUVROWUP2_LINEAR_SSSE3
|
||||
#define HAS_SCALEUVROWUP2_BILINEAR_SSSE3
|
||||
#define HAS_SCALEUVROWUP2_LINEAR_16_SSE41
|
||||
#define HAS_SCALEUVROWUP2_BILINEAR_16_SSE41
|
||||
#endif
|
||||
|
||||
// The following are available for gcc/clang x86 platforms, but
|
||||
// require clang 3.4 or gcc 4.7.
|
||||
// TODO(fbarchard): Port to Visual C
|
||||
#if !defined(LIBYUV_DISABLE_X86) && \
|
||||
(defined(__x86_64__) || defined(__i386__)) && \
|
||||
(defined(CLANG_HAS_AVX2) || defined(GCC_HAS_AVX2))
|
||||
#define HAS_SCALEUVROWDOWN2BOX_AVX2
|
||||
#define HAS_SCALEROWUP2_LINEAR_AVX2
|
||||
#define HAS_SCALEROWUP2_BILINEAR_AVX2
|
||||
#define HAS_SCALEROWUP2_LINEAR_12_AVX2
|
||||
#define HAS_SCALEROWUP2_BILINEAR_12_AVX2
|
||||
#define HAS_SCALEROWUP2_LINEAR_16_AVX2
|
||||
#define HAS_SCALEROWUP2_BILINEAR_16_AVX2
|
||||
#define HAS_SCALEUVROWUP2_LINEAR_AVX2
|
||||
#define HAS_SCALEUVROWUP2_BILINEAR_AVX2
|
||||
#define HAS_SCALEUVROWUP2_LINEAR_16_AVX2
|
||||
#define HAS_SCALEUVROWUP2_BILINEAR_16_AVX2
|
||||
#endif
|
||||
|
||||
// The following are available on all x86 platforms, but
|
||||
// require VS2012, clang 3.4 or gcc 4.7.
|
||||
// The code supports NaCL but requires a new compiler and validator.
|
||||
#if !defined(LIBYUV_DISABLE_X86) && \
|
||||
(defined(VISUALC_HAS_AVX2) || defined(CLANG_HAS_AVX2) || \
|
||||
defined(GCC_HAS_AVX2))
|
||||
#define HAS_SCALEADDROW_AVX2
|
||||
#define HAS_SCALEROWDOWN2_AVX2
|
||||
#define HAS_SCALEROWDOWN4_AVX2
|
||||
#endif
|
||||
|
||||
// Scale ARGB vertically with bilinear interpolation.
|
||||
void ScalePlaneVertical(int src_height,
|
||||
int dst_width,
|
||||
int dst_height,
|
||||
int src_stride,
|
||||
int dst_stride,
|
||||
const uint8_t *src_argb,
|
||||
uint8_t *dst_argb,
|
||||
int x,
|
||||
int y,
|
||||
int dy,
|
||||
int bpp,
|
||||
enum FilterMode filtering);
|
||||
|
||||
// Simplify the filtering based on scale factors.
|
||||
enum FilterMode ScaleFilterReduce(int src_width,
|
||||
int src_height,
|
||||
int dst_width,
|
||||
int dst_height,
|
||||
enum FilterMode filtering);
|
||||
|
||||
// Divide num by div and return as 16.16 fixed point result.
|
||||
int FixedDiv_X86(int num, int div);
|
||||
|
||||
int FixedDiv1_X86(int num, int div);
|
||||
|
||||
#ifdef HAS_FIXEDDIV_X86
|
||||
#define FixedDiv FixedDiv_X86
|
||||
#define FixedDiv1 FixedDiv1_X86
|
||||
#endif
|
||||
|
||||
// Compute slope values for stepping.
|
||||
void ScaleSlope(int src_width,
|
||||
int src_height,
|
||||
int dst_width,
|
||||
int dst_height,
|
||||
enum FilterMode filtering,
|
||||
int *x,
|
||||
int *y,
|
||||
int *dx,
|
||||
int *dy);
|
||||
|
||||
void ScaleRowDown2_C(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown2Linear_C(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown2Box_C(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown2Box_Odd_C(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown4_C(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown4Box_C(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown34_C(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown34_0_Box_C(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *d,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown34_1_Box_C(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *d,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Linear_C(const uint8_t *src_ptr,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Bilinear_C(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Linear_16_C(const uint16_t *src_ptr,
|
||||
uint16_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Bilinear_16_C(const uint16_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint16_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Linear_Any_C(const uint8_t *src_ptr,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Bilinear_Any_C(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Linear_16_Any_C(const uint16_t *src_ptr,
|
||||
uint16_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Bilinear_16_Any_C(const uint16_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint16_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width);
|
||||
|
||||
void ScaleCols_C(uint8_t *dst_ptr,
|
||||
const uint8_t *src_ptr,
|
||||
int dst_width,
|
||||
int x,
|
||||
int dx);
|
||||
|
||||
void ScaleColsUp2_C(uint8_t *dst_ptr,
|
||||
const uint8_t *src_ptr,
|
||||
int dst_width,
|
||||
int,
|
||||
int);
|
||||
|
||||
void ScaleFilterCols_C(uint8_t *dst_ptr,
|
||||
const uint8_t *src_ptr,
|
||||
int dst_width,
|
||||
int x,
|
||||
int dx);
|
||||
|
||||
void ScaleFilterCols64_C(uint8_t *dst_ptr,
|
||||
const uint8_t *src_ptr,
|
||||
int dst_width,
|
||||
int x32,
|
||||
int dx);
|
||||
|
||||
void ScaleRowDown38_C(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown38_3_Box_C(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown38_2_Box_C(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleAddRow_C(const uint8_t *src_ptr, uint16_t *dst_ptr, int src_width);
|
||||
|
||||
void ScaleUVRowDown2_C(const uint8_t *src_uv,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_uv,
|
||||
int dst_width);
|
||||
|
||||
void ScaleUVRowDown2Linear_C(const uint8_t *src_uv,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_uv,
|
||||
int dst_width);
|
||||
|
||||
void ScaleUVRowDown2Box_C(const uint8_t *src_uv,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_uv,
|
||||
int dst_width);
|
||||
|
||||
void ScaleUVRowDownEven_C(const uint8_t *src_uv,
|
||||
ptrdiff_t src_stride,
|
||||
int src_stepx,
|
||||
uint8_t *dst_uv,
|
||||
int dst_width);
|
||||
|
||||
void ScaleUVRowUp2_Linear_C(const uint8_t *src_ptr,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleUVRowUp2_Bilinear_C(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width);
|
||||
|
||||
void ScaleUVRowUp2_Linear_Any_C(const uint8_t *src_ptr,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleUVRowUp2_Bilinear_Any_C(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width);
|
||||
|
||||
void ScaleUVRowUp2_Linear_16_C(const uint16_t *src_ptr,
|
||||
uint16_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleUVRowUp2_Bilinear_16_C(const uint16_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint16_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width);
|
||||
|
||||
void ScaleUVRowUp2_Linear_16_Any_C(const uint16_t *src_ptr,
|
||||
uint16_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleUVRowUp2_Bilinear_16_Any_C(const uint16_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint16_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width);
|
||||
|
||||
// Specialized scalers for x86.
|
||||
void ScaleRowDown2_SSSE3(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown2Linear_SSSE3(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown2Box_SSSE3(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown2_AVX2(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown2Linear_AVX2(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown2Box_AVX2(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown4_SSSE3(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown4Box_SSSE3(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown4_AVX2(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown4Box_AVX2(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown34_SSSE3(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown34_1_Box_SSSE3(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown34_0_Box_SSSE3(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown38_SSSE3(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown38_3_Box_SSSE3(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown38_2_Box_SSSE3(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Linear_SSE2(const uint8_t *src_ptr,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Bilinear_SSE2(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Linear_12_SSSE3(const uint16_t *src_ptr,
|
||||
uint16_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Bilinear_12_SSSE3(const uint16_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint16_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Linear_16_SSE2(const uint16_t *src_ptr,
|
||||
uint16_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Bilinear_16_SSE2(const uint16_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint16_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Linear_SSSE3(const uint8_t *src_ptr,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Bilinear_SSSE3(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Linear_AVX2(const uint8_t *src_ptr,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Bilinear_AVX2(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Linear_12_AVX2(const uint16_t *src_ptr,
|
||||
uint16_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Bilinear_12_AVX2(const uint16_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint16_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Linear_16_AVX2(const uint16_t *src_ptr,
|
||||
uint16_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Bilinear_16_AVX2(const uint16_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint16_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Linear_Any_SSE2(const uint8_t *src_ptr,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Bilinear_Any_SSE2(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Linear_12_Any_SSSE3(const uint16_t *src_ptr,
|
||||
uint16_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Bilinear_12_Any_SSSE3(const uint16_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint16_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Linear_16_Any_SSE2(const uint16_t *src_ptr,
|
||||
uint16_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Bilinear_16_Any_SSE2(const uint16_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint16_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Linear_Any_SSSE3(const uint8_t *src_ptr,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Bilinear_Any_SSSE3(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Linear_Any_AVX2(const uint8_t *src_ptr,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Bilinear_Any_AVX2(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Linear_12_Any_AVX2(const uint16_t *src_ptr,
|
||||
uint16_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Bilinear_12_Any_AVX2(const uint16_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint16_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Linear_16_Any_AVX2(const uint16_t *src_ptr,
|
||||
uint16_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowUp2_Bilinear_16_Any_AVX2(const uint16_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint16_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown2_Any_SSSE3(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown2Linear_Any_SSSE3(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown2Box_Any_SSSE3(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown2Box_Odd_SSSE3(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown2_Any_AVX2(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown2Linear_Any_AVX2(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown2Box_Any_AVX2(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown2Box_Odd_AVX2(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown4_Any_SSSE3(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown4Box_Any_SSSE3(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown4_Any_AVX2(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown4Box_Any_AVX2(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown34_Any_SSSE3(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown34_1_Box_Any_SSSE3(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown34_0_Box_Any_SSSE3(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown38_Any_SSSE3(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown38_3_Box_Any_SSSE3(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleRowDown38_2_Box_Any_SSSE3(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleAddRow_SSE2(const uint8_t *src_ptr, uint16_t *dst_ptr, int src_width);
|
||||
|
||||
void ScaleAddRow_AVX2(const uint8_t *src_ptr, uint16_t *dst_ptr, int src_width);
|
||||
|
||||
void ScaleAddRow_Any_SSE2(const uint8_t *src_ptr,
|
||||
uint16_t *dst_ptr,
|
||||
int src_width);
|
||||
|
||||
void ScaleAddRow_Any_AVX2(const uint8_t *src_ptr,
|
||||
uint16_t *dst_ptr,
|
||||
int src_width);
|
||||
|
||||
void ScaleFilterCols_SSSE3(uint8_t *dst_ptr,
|
||||
const uint8_t *src_ptr,
|
||||
int dst_width,
|
||||
int x,
|
||||
int dx);
|
||||
|
||||
void ScaleColsUp2_SSE2(uint8_t *dst_ptr,
|
||||
const uint8_t *src_ptr,
|
||||
int dst_width,
|
||||
int x,
|
||||
int dx);
|
||||
|
||||
// UV Row functions
|
||||
void ScaleUVRowDown2Box_SSSE3(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_uv,
|
||||
int dst_width);
|
||||
|
||||
void ScaleUVRowDown2Box_AVX2(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_uv,
|
||||
int dst_width);
|
||||
|
||||
void ScaleUVRowDown2Box_Any_SSSE3(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleUVRowDown2Box_Any_AVX2(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleUVRowUp2_Linear_SSSE3(const uint8_t *src_ptr,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleUVRowUp2_Bilinear_SSSE3(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width);
|
||||
|
||||
void ScaleUVRowUp2_Linear_Any_SSSE3(const uint8_t *src_ptr,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleUVRowUp2_Bilinear_Any_SSSE3(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width);
|
||||
|
||||
void ScaleUVRowUp2_Linear_AVX2(const uint8_t *src_ptr,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleUVRowUp2_Bilinear_AVX2(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width);
|
||||
|
||||
void ScaleUVRowUp2_Linear_Any_AVX2(const uint8_t *src_ptr,
|
||||
uint8_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleUVRowUp2_Bilinear_Any_AVX2(const uint8_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint8_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width);
|
||||
|
||||
void ScaleUVRowUp2_Linear_16_SSE41(const uint16_t *src_ptr,
|
||||
uint16_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleUVRowUp2_Bilinear_16_SSE41(const uint16_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint16_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width);
|
||||
|
||||
void ScaleUVRowUp2_Linear_16_Any_SSE41(const uint16_t *src_ptr,
|
||||
uint16_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleUVRowUp2_Bilinear_16_Any_SSE41(const uint16_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint16_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width);
|
||||
|
||||
void ScaleUVRowUp2_Linear_16_AVX2(const uint16_t *src_ptr,
|
||||
uint16_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleUVRowUp2_Bilinear_16_AVX2(const uint16_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint16_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width);
|
||||
|
||||
void ScaleUVRowUp2_Linear_16_Any_AVX2(const uint16_t *src_ptr,
|
||||
uint16_t *dst_ptr,
|
||||
int dst_width);
|
||||
|
||||
void ScaleUVRowUp2_Bilinear_16_Any_AVX2(const uint16_t *src_ptr,
|
||||
ptrdiff_t src_stride,
|
||||
uint16_t *dst_ptr,
|
||||
ptrdiff_t dst_stride,
|
||||
int dst_width);
|
||||
|
||||
#endif // INCLUDE_LIBYUV_SCALE_ROW_H_
|
||||
16
pkg/encoder/yuv/libyuv/version.h
Normal file
16
pkg/encoder/yuv/libyuv/version.h
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Copyright 2012 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_LIBYUV_VERSION_H_
|
||||
#define INCLUDE_LIBYUV_VERSION_H_
|
||||
|
||||
#define LIBYUV_VERSION 1875
|
||||
|
||||
#endif // INCLUDE_LIBYUV_VERSION_H_
|
||||
50
pkg/encoder/yuv/libyuv/video_common.c
Normal file
50
pkg/encoder/yuv/libyuv/video_common.c
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright 2011 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "video_common.h"
|
||||
|
||||
struct FourCCAliasEntry {
|
||||
uint32_t alias;
|
||||
uint32_t canonical;
|
||||
};
|
||||
|
||||
#define NUM_ALIASES 18
|
||||
static const struct FourCCAliasEntry kFourCCAliases[NUM_ALIASES] = {
|
||||
{FOURCC_IYUV, FOURCC_I420},
|
||||
{FOURCC_YU12, FOURCC_I420},
|
||||
{FOURCC_YU16, FOURCC_I422},
|
||||
{FOURCC_YU24, FOURCC_I444},
|
||||
{FOURCC_YUYV, FOURCC_YUY2},
|
||||
{FOURCC_YUVS, FOURCC_YUY2}, // kCMPixelFormat_422YpCbCr8_yuvs
|
||||
{FOURCC_HDYC, FOURCC_UYVY},
|
||||
{FOURCC_2VUY, FOURCC_UYVY}, // kCMPixelFormat_422YpCbCr8
|
||||
{FOURCC_JPEG, FOURCC_MJPG}, // Note: JPEG has DHT while MJPG does not.
|
||||
{FOURCC_DMB1, FOURCC_MJPG},
|
||||
{FOURCC_BA81, FOURCC_BGGR}, // deprecated.
|
||||
{FOURCC_RGB3, FOURCC_RAW},
|
||||
{FOURCC_BGR3, FOURCC_24BG},
|
||||
{FOURCC_CM32, FOURCC_BGRA}, // kCMPixelFormat_32ARGB
|
||||
{FOURCC_CM24, FOURCC_RAW}, // kCMPixelFormat_24RGB
|
||||
{FOURCC_L555, FOURCC_RGBO}, // kCMPixelFormat_16LE555
|
||||
{FOURCC_L565, FOURCC_RGBP}, // kCMPixelFormat_16LE565
|
||||
{FOURCC_5551, FOURCC_RGBO}, // kCMPixelFormat_16LE5551
|
||||
};
|
||||
|
||||
LIBYUV_API
|
||||
uint32_t CanonicalFourCC(uint32_t fourcc) {
|
||||
int i;
|
||||
for (i = 0; i < NUM_ALIASES; ++i) {
|
||||
if (kFourCCAliases[i].alias == fourcc) {
|
||||
return kFourCCAliases[i].canonical;
|
||||
}
|
||||
}
|
||||
// Not an alias, so return it as-is.
|
||||
return fourcc;
|
||||
}
|
||||
212
pkg/encoder/yuv/libyuv/video_common.h
Normal file
212
pkg/encoder/yuv/libyuv/video_common.h
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
* Copyright 2011 The LibYuv Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
// Common definitions for video, including fourcc and VideoFormat.
|
||||
|
||||
#ifndef INCLUDE_LIBYUV_VIDEO_COMMON_H_
|
||||
#define INCLUDE_LIBYUV_VIDEO_COMMON_H_
|
||||
|
||||
#include "basic_types.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Definition of FourCC codes
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Convert four characters to a FourCC code.
|
||||
// Needs to be a macro otherwise the OS X compiler complains when the kFormat*
|
||||
// constants are used in a switch.
|
||||
#ifdef __cplusplus
|
||||
#define FOURCC(a, b, c, d) \
|
||||
((static_cast<uint32_t>(a)) | (static_cast<uint32_t>(b) << 8) | \
|
||||
(static_cast<uint32_t>(c) << 16) | /* NOLINT */ \
|
||||
(static_cast<uint32_t>(d) << 24)) /* NOLINT */
|
||||
#else
|
||||
#define FOURCC(a, b, c, d) \
|
||||
(((uint32_t)(a)) | ((uint32_t)(b) << 8) | /* NOLINT */ \
|
||||
((uint32_t)(c) << 16) | ((uint32_t)(d) << 24)) /* NOLINT */
|
||||
#endif
|
||||
|
||||
// Some pages discussing FourCC codes:
|
||||
// http://www.fourcc.org/yuv.php
|
||||
// http://v4l2spec.bytesex.org/spec/book1.htm
|
||||
// http://developer.apple.com/quicktime/icefloe/dispatch020.html
|
||||
// http://msdn.microsoft.com/library/windows/desktop/dd206750.aspx#nv12
|
||||
// http://people.xiph.org/~xiphmont/containers/nut/nut4cc.txt
|
||||
|
||||
// FourCC codes grouped according to implementation efficiency.
|
||||
// Primary formats should convert in 1 efficient step.
|
||||
// Secondary formats are converted in 2 steps.
|
||||
// Auxilliary formats call primary converters.
|
||||
enum FourCC {
|
||||
// 10 Primary YUV formats: 5 planar, 2 biplanar, 2 packed.
|
||||
FOURCC_I420 = FOURCC('I', '4', '2', '0'),
|
||||
FOURCC_I422 = FOURCC('I', '4', '2', '2'),
|
||||
FOURCC_I444 = FOURCC('I', '4', '4', '4'),
|
||||
FOURCC_I400 = FOURCC('I', '4', '0', '0'),
|
||||
FOURCC_NV21 = FOURCC('N', 'V', '2', '1'),
|
||||
FOURCC_NV12 = FOURCC('N', 'V', '1', '2'),
|
||||
FOURCC_YUY2 = FOURCC('Y', 'U', 'Y', '2'),
|
||||
FOURCC_UYVY = FOURCC('U', 'Y', 'V', 'Y'),
|
||||
FOURCC_I010 = FOURCC('I', '0', '1', '0'), // bt.601 10 bit 420
|
||||
FOURCC_I210 = FOURCC('I', '2', '1', '0'), // bt.601 10 bit 422
|
||||
|
||||
// 1 Secondary YUV format: row biplanar. deprecated.
|
||||
FOURCC_M420 = FOURCC('M', '4', '2', '0'),
|
||||
|
||||
// 13 Primary RGB formats: 4 32 bpp, 2 24 bpp, 3 16 bpp, 1 10 bpc 2 64 bpp
|
||||
FOURCC_ARGB = FOURCC('A', 'R', 'G', 'B'),
|
||||
FOURCC_BGRA = FOURCC('B', 'G', 'R', 'A'),
|
||||
FOURCC_ABGR = FOURCC('A', 'B', 'G', 'R'),
|
||||
FOURCC_AR30 = FOURCC('A', 'R', '3', '0'), // 10 bit per channel. 2101010.
|
||||
FOURCC_AB30 = FOURCC('A', 'B', '3', '0'), // ABGR version of 10 bit
|
||||
FOURCC_AR64 = FOURCC('A', 'R', '6', '4'), // 16 bit per channel.
|
||||
FOURCC_AB64 = FOURCC('A', 'B', '6', '4'), // ABGR version of 16 bit
|
||||
FOURCC_24BG = FOURCC('2', '4', 'B', 'G'),
|
||||
FOURCC_RAW = FOURCC('r', 'a', 'w', ' '),
|
||||
FOURCC_RGBA = FOURCC('R', 'G', 'B', 'A'),
|
||||
FOURCC_RGBP = FOURCC('R', 'G', 'B', 'P'), // rgb565 LE.
|
||||
FOURCC_RGBO = FOURCC('R', 'G', 'B', 'O'), // argb1555 LE.
|
||||
FOURCC_R444 = FOURCC('R', '4', '4', '4'), // argb4444 LE.
|
||||
|
||||
// 1 Primary Compressed YUV format.
|
||||
FOURCC_MJPG = FOURCC('M', 'J', 'P', 'G'),
|
||||
|
||||
// 14 Auxiliary YUV variations: 3 with U and V planes are swapped, 1 Alias.
|
||||
FOURCC_YV12 = FOURCC('Y', 'V', '1', '2'),
|
||||
FOURCC_YV16 = FOURCC('Y', 'V', '1', '6'),
|
||||
FOURCC_YV24 = FOURCC('Y', 'V', '2', '4'),
|
||||
FOURCC_YU12 = FOURCC('Y', 'U', '1', '2'), // Linux version of I420.
|
||||
FOURCC_J420 =
|
||||
FOURCC('J', '4', '2', '0'), // jpeg (bt.601 full), unofficial fourcc
|
||||
FOURCC_J422 =
|
||||
FOURCC('J', '4', '2', '2'), // jpeg (bt.601 full), unofficial fourcc
|
||||
FOURCC_J444 =
|
||||
FOURCC('J', '4', '4', '4'), // jpeg (bt.601 full), unofficial fourcc
|
||||
FOURCC_J400 =
|
||||
FOURCC('J', '4', '0', '0'), // jpeg (bt.601 full), unofficial fourcc
|
||||
FOURCC_F420 = FOURCC('F', '4', '2', '0'), // bt.709 full, unofficial fourcc
|
||||
FOURCC_F422 = FOURCC('F', '4', '2', '2'), // bt.709 full, unofficial fourcc
|
||||
FOURCC_F444 = FOURCC('F', '4', '4', '4'), // bt.709 full, unofficial fourcc
|
||||
FOURCC_H420 = FOURCC('H', '4', '2', '0'), // bt.709, unofficial fourcc
|
||||
FOURCC_H422 = FOURCC('H', '4', '2', '2'), // bt.709, unofficial fourcc
|
||||
FOURCC_H444 = FOURCC('H', '4', '4', '4'), // bt.709, unofficial fourcc
|
||||
FOURCC_U420 = FOURCC('U', '4', '2', '0'), // bt.2020, unofficial fourcc
|
||||
FOURCC_U422 = FOURCC('U', '4', '2', '2'), // bt.2020, unofficial fourcc
|
||||
FOURCC_U444 = FOURCC('U', '4', '4', '4'), // bt.2020, unofficial fourcc
|
||||
FOURCC_F010 = FOURCC('F', '0', '1', '0'), // bt.709 full range 10 bit 420
|
||||
FOURCC_H010 = FOURCC('H', '0', '1', '0'), // bt.709 10 bit 420
|
||||
FOURCC_U010 = FOURCC('U', '0', '1', '0'), // bt.2020 10 bit 420
|
||||
FOURCC_F210 = FOURCC('F', '2', '1', '0'), // bt.709 full range 10 bit 422
|
||||
FOURCC_H210 = FOURCC('H', '2', '1', '0'), // bt.709 10 bit 422
|
||||
FOURCC_U210 = FOURCC('U', '2', '1', '0'), // bt.2020 10 bit 422
|
||||
FOURCC_P010 = FOURCC('P', '0', '1', '0'),
|
||||
FOURCC_P210 = FOURCC('P', '2', '1', '0'),
|
||||
|
||||
// 14 Auxiliary aliases. CanonicalFourCC() maps these to canonical fourcc.
|
||||
FOURCC_IYUV = FOURCC('I', 'Y', 'U', 'V'), // Alias for I420.
|
||||
FOURCC_YU16 = FOURCC('Y', 'U', '1', '6'), // Alias for I422.
|
||||
FOURCC_YU24 = FOURCC('Y', 'U', '2', '4'), // Alias for I444.
|
||||
FOURCC_YUYV = FOURCC('Y', 'U', 'Y', 'V'), // Alias for YUY2.
|
||||
FOURCC_YUVS = FOURCC('y', 'u', 'v', 's'), // Alias for YUY2 on Mac.
|
||||
FOURCC_HDYC = FOURCC('H', 'D', 'Y', 'C'), // Alias for UYVY.
|
||||
FOURCC_2VUY = FOURCC('2', 'v', 'u', 'y'), // Alias for UYVY on Mac.
|
||||
FOURCC_JPEG = FOURCC('J', 'P', 'E', 'G'), // Alias for MJPG.
|
||||
FOURCC_DMB1 = FOURCC('d', 'm', 'b', '1'), // Alias for MJPG on Mac.
|
||||
FOURCC_BA81 = FOURCC('B', 'A', '8', '1'), // Alias for BGGR.
|
||||
FOURCC_RGB3 = FOURCC('R', 'G', 'B', '3'), // Alias for RAW.
|
||||
FOURCC_BGR3 = FOURCC('B', 'G', 'R', '3'), // Alias for 24BG.
|
||||
FOURCC_CM32 = FOURCC(0, 0, 0, 32), // Alias for BGRA kCMPixelFormat_32ARGB
|
||||
FOURCC_CM24 = FOURCC(0, 0, 0, 24), // Alias for RAW kCMPixelFormat_24RGB
|
||||
FOURCC_L555 = FOURCC('L', '5', '5', '5'), // Alias for RGBO.
|
||||
FOURCC_L565 = FOURCC('L', '5', '6', '5'), // Alias for RGBP.
|
||||
FOURCC_5551 = FOURCC('5', '5', '5', '1'), // Alias for RGBO.
|
||||
|
||||
// deprecated formats. Not supported, but defined for backward compatibility.
|
||||
FOURCC_I411 = FOURCC('I', '4', '1', '1'),
|
||||
FOURCC_Q420 = FOURCC('Q', '4', '2', '0'),
|
||||
FOURCC_RGGB = FOURCC('R', 'G', 'G', 'B'),
|
||||
FOURCC_BGGR = FOURCC('B', 'G', 'G', 'R'),
|
||||
FOURCC_GRBG = FOURCC('G', 'R', 'B', 'G'),
|
||||
FOURCC_GBRG = FOURCC('G', 'B', 'R', 'G'),
|
||||
FOURCC_H264 = FOURCC('H', '2', '6', '4'),
|
||||
|
||||
// Match any fourcc.
|
||||
FOURCC_ANY = -1,
|
||||
};
|
||||
|
||||
enum FourCCBpp {
|
||||
// Canonical fourcc codes used in our code.
|
||||
FOURCC_BPP_I420 = 12,
|
||||
FOURCC_BPP_I422 = 16,
|
||||
FOURCC_BPP_I444 = 24,
|
||||
FOURCC_BPP_I411 = 12,
|
||||
FOURCC_BPP_I400 = 8,
|
||||
FOURCC_BPP_NV21 = 12,
|
||||
FOURCC_BPP_NV12 = 12,
|
||||
FOURCC_BPP_YUY2 = 16,
|
||||
FOURCC_BPP_UYVY = 16,
|
||||
FOURCC_BPP_M420 = 12, // deprecated
|
||||
FOURCC_BPP_Q420 = 12,
|
||||
FOURCC_BPP_ARGB = 32,
|
||||
FOURCC_BPP_BGRA = 32,
|
||||
FOURCC_BPP_ABGR = 32,
|
||||
FOURCC_BPP_RGBA = 32,
|
||||
FOURCC_BPP_AR30 = 32,
|
||||
FOURCC_BPP_AB30 = 32,
|
||||
FOURCC_BPP_AR64 = 64,
|
||||
FOURCC_BPP_AB64 = 64,
|
||||
FOURCC_BPP_24BG = 24,
|
||||
FOURCC_BPP_RAW = 24,
|
||||
FOURCC_BPP_RGBP = 16,
|
||||
FOURCC_BPP_RGBO = 16,
|
||||
FOURCC_BPP_R444 = 16,
|
||||
FOURCC_BPP_RGGB = 8,
|
||||
FOURCC_BPP_BGGR = 8,
|
||||
FOURCC_BPP_GRBG = 8,
|
||||
FOURCC_BPP_GBRG = 8,
|
||||
FOURCC_BPP_YV12 = 12,
|
||||
FOURCC_BPP_YV16 = 16,
|
||||
FOURCC_BPP_YV24 = 24,
|
||||
FOURCC_BPP_YU12 = 12,
|
||||
FOURCC_BPP_J420 = 12,
|
||||
FOURCC_BPP_J400 = 8,
|
||||
FOURCC_BPP_H420 = 12,
|
||||
FOURCC_BPP_H422 = 16,
|
||||
FOURCC_BPP_I010 = 15,
|
||||
FOURCC_BPP_I210 = 20,
|
||||
FOURCC_BPP_H010 = 15,
|
||||
FOURCC_BPP_H210 = 20,
|
||||
FOURCC_BPP_P010 = 15,
|
||||
FOURCC_BPP_P210 = 20,
|
||||
FOURCC_BPP_MJPG = 0, // 0 means unknown.
|
||||
FOURCC_BPP_H264 = 0,
|
||||
FOURCC_BPP_IYUV = 12,
|
||||
FOURCC_BPP_YU16 = 16,
|
||||
FOURCC_BPP_YU24 = 24,
|
||||
FOURCC_BPP_YUYV = 16,
|
||||
FOURCC_BPP_YUVS = 16,
|
||||
FOURCC_BPP_HDYC = 16,
|
||||
FOURCC_BPP_2VUY = 16,
|
||||
FOURCC_BPP_JPEG = 1,
|
||||
FOURCC_BPP_DMB1 = 1,
|
||||
FOURCC_BPP_BA81 = 8,
|
||||
FOURCC_BPP_RGB3 = 24,
|
||||
FOURCC_BPP_BGR3 = 24,
|
||||
FOURCC_BPP_CM32 = 32,
|
||||
FOURCC_BPP_CM24 = 24,
|
||||
|
||||
// Match any fourcc.
|
||||
FOURCC_BPP_ANY = 0, // 0 means unknown.
|
||||
};
|
||||
|
||||
// Converts fourcc aliases into canonical ones.
|
||||
LIBYUV_API uint32_t CanonicalFourCC(uint32_t fourcc);
|
||||
|
||||
#endif // INCLUDE_LIBYUV_VIDEO_COMMON_H_
|
||||
|
|
@ -1,130 +0,0 @@
|
|||
#include "yuv.h"
|
||||
|
||||
#define Y601_STUDIO 1
|
||||
|
||||
// BT.601 STUDIO
|
||||
|
||||
#ifdef Y601_STUDIO
|
||||
// 66*R+129*G+25*B
|
||||
static __inline int Y(uint8_t *__restrict rgb) {
|
||||
int R = *rgb;
|
||||
int G = *(rgb+1);
|
||||
int B = *(rgb+2);
|
||||
return (66*R+129*G+25*B+128)>>8;
|
||||
}
|
||||
|
||||
// 112*B-38*R-74G
|
||||
static __inline int U(uint8_t *__restrict rgb) {
|
||||
int R = *rgb;
|
||||
int G = *(rgb+1);
|
||||
int B = *(rgb+2);
|
||||
return (-38*R-74*G+112*B+128) >> 8;
|
||||
}
|
||||
|
||||
// 112*R-94*G-18*B
|
||||
static __inline int V(uint8_t *__restrict rgb) {
|
||||
int R = 56**(rgb);
|
||||
int G = 47**(rgb+1);
|
||||
int B = *(rgb+2);
|
||||
return (R-G-(B+(B<<3))+64) >> 7;
|
||||
}
|
||||
|
||||
static const int Y_MIN = 16;
|
||||
|
||||
#else
|
||||
|
||||
// BT.601 FULL
|
||||
|
||||
// 77*R+150*G+29*B
|
||||
static __inline int Y(uint8_t *rgb) {
|
||||
int R = 77**(rgb);
|
||||
int G = 150**(rgb+1);
|
||||
int B = 29**(rgb+2);
|
||||
return (R+G+B+128) >> 8;
|
||||
}
|
||||
|
||||
// 127*B-43*R-84*G
|
||||
static __inline int U(uint8_t *rgb) {
|
||||
int R = 43**(rgb);
|
||||
int G = 84**(rgb+1);
|
||||
int B = 127**(rgb+2);
|
||||
return (-R-G+B+128) >> 8;
|
||||
}
|
||||
|
||||
// 127*R-106*G-21*B
|
||||
static __inline int V(uint8_t *rgb) {
|
||||
int R = 127**rgb;
|
||||
int G = -106**(rgb+1);
|
||||
int B = -21**(rgb+2);
|
||||
return (G+B+R+128) >> 8;
|
||||
}
|
||||
|
||||
static const int Y_MIN = 0;
|
||||
#endif
|
||||
|
||||
static __inline void _y(uint8_t *__restrict p, uint8_t *__restrict y, int size) {
|
||||
do {
|
||||
*y++ = Y(p) + Y_MIN;
|
||||
p += 4;
|
||||
} while (--size);
|
||||
}
|
||||
|
||||
// It will take an average color from the 2x2 pixel group for chroma values.
|
||||
// X X X X
|
||||
// O O
|
||||
// X X X X
|
||||
static __inline void _4uv(uint8_t * __restrict p, uint8_t * __restrict u, uint8_t * __restrict v, const int w, const int h) {
|
||||
uint8_t *p2, *p3, *p4;
|
||||
const int row = w << 2;
|
||||
const int next = 4;
|
||||
|
||||
int x = w, y = h, sumU = 0, sumV = 0;
|
||||
while (y > 0) {
|
||||
while (x > 0) {
|
||||
// xx..
|
||||
// ....
|
||||
p2 = p+next;
|
||||
sumU = U(p) + U(p2);
|
||||
sumV = V(p) + V(p2);
|
||||
// ....
|
||||
// xx..
|
||||
p3 = p+row;
|
||||
p4 = p3+next;
|
||||
sumU += U(p3) + U(p4);
|
||||
sumV += V(p3) + V(p4);
|
||||
*u++ = 128 + (sumU >> 2);
|
||||
*v++ = 128 + (sumV >> 2);
|
||||
// ..x.
|
||||
p += 8;
|
||||
x -= 2;
|
||||
}
|
||||
p += row;
|
||||
y -= 2;
|
||||
x = w;
|
||||
}
|
||||
}
|
||||
|
||||
// Converts RGBA image to YUV (I420) with BT.601 studio color range.
|
||||
void rgbaToYuv(void *__restrict destination, void *__restrict source, const int w, const int h) {
|
||||
const int image_size = w * h;
|
||||
uint8_t *src = source;
|
||||
uint8_t *dst_y = destination;
|
||||
uint8_t *dst_u = destination + image_size;
|
||||
uint8_t *dst_v = destination + image_size + image_size / 4;
|
||||
_y(src, dst_y, image_size);
|
||||
src = source;
|
||||
_4uv(source, dst_u, dst_v, w, h);
|
||||
}
|
||||
|
||||
void luma(void *__restrict destination, void *__restrict source, const int pos, const int w, const int h) {
|
||||
uint8_t *rgba = source + 4 * pos;
|
||||
uint8_t *dst = destination + pos;
|
||||
_y(rgba, dst, w*h);
|
||||
}
|
||||
|
||||
void chroma(void *__restrict dst, void *__restrict source, const int pos, const int deu, const int dev, const int w, const int h) {
|
||||
uint8_t *src = source + 4 * pos;
|
||||
uint8_t *dst_u = dst + deu + pos / 4;
|
||||
uint8_t *dst_v = dst + dev + pos / 4;
|
||||
_4uv(src, dst_u, dst_v, w, h);
|
||||
}
|
||||
|
|
@ -3,123 +3,80 @@ package yuv
|
|||
import (
|
||||
"image"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/giongto35/cloud-game/v3/pkg/encoder/yuv/libyuv"
|
||||
)
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -Wall
|
||||
#include "yuv.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
type ImgProcessor interface {
|
||||
Process(rgba *image.RGBA) []byte
|
||||
Put(*[]byte)
|
||||
type Conv struct {
|
||||
w, h int
|
||||
sw, sh int
|
||||
scale float64
|
||||
pool sync.Pool
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
Threads int
|
||||
type RawFrame struct {
|
||||
Data []byte
|
||||
Stride int
|
||||
W, H int
|
||||
}
|
||||
|
||||
type processor struct {
|
||||
w, h int
|
||||
type PixFmt uint32
|
||||
|
||||
// cache
|
||||
ww C.int
|
||||
pool sync.Pool
|
||||
const FourccRgbp = libyuv.FourccRgbp
|
||||
const FourccArgb = libyuv.FourccArgb
|
||||
const FourccAbgr = libyuv.FourccAbgr
|
||||
|
||||
func NewYuvConv(w, h int, scale float64) Conv {
|
||||
if scale < 1 {
|
||||
scale = 1
|
||||
}
|
||||
sw, sh := round(w, scale), round(h, scale)
|
||||
bufSize := int(float64(sw) * float64(sh) * 1.5)
|
||||
return Conv{
|
||||
w: w, h: h, sw: sw, sh: sh, scale: scale,
|
||||
pool: sync.Pool{New: func() any { b := make([]byte, bufSize); return &b }},
|
||||
}
|
||||
}
|
||||
|
||||
type threadedProcessor struct {
|
||||
*processor
|
||||
|
||||
// threading
|
||||
threads int
|
||||
chunk int
|
||||
|
||||
// cache
|
||||
chromaU C.int
|
||||
chromaV C.int
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
// NewYuvImgProcessor creates new YUV image converter from RGBA.
|
||||
func NewYuvImgProcessor(w, h int, opts *Options) ImgProcessor {
|
||||
bufSize := int(float32(w*h) * 1.5)
|
||||
|
||||
processor := processor{
|
||||
w: w,
|
||||
h: h,
|
||||
ww: C.int(w),
|
||||
pool: sync.Pool{New: func() any {
|
||||
b := make([]byte, bufSize)
|
||||
return &b
|
||||
}},
|
||||
// Process converts an image to YUV I420 format inside the internal buffer.
|
||||
func (c *Conv) Process(frame RawFrame, rot uint, pf PixFmt) []byte {
|
||||
dx, dy := c.w, c.h // dest
|
||||
cx, cy := c.w, c.h // crop
|
||||
if rot == 90 || rot == 270 {
|
||||
cx, cy = cy, cx
|
||||
}
|
||||
|
||||
if opts != nil && opts.Threads > 0 {
|
||||
// chunks the image evenly
|
||||
chunk := h / opts.Threads
|
||||
if chunk%2 != 0 {
|
||||
chunk--
|
||||
}
|
||||
|
||||
return &threadedProcessor{
|
||||
chromaU: C.int(w * h),
|
||||
chromaV: C.int(w*h + w*h/4),
|
||||
chunk: chunk,
|
||||
processor: &processor,
|
||||
threads: opts.Threads,
|
||||
wg: sync.WaitGroup{},
|
||||
}
|
||||
stride := frame.Stride >> 2
|
||||
if pf == PixFmt(libyuv.FourccRgbp) {
|
||||
stride = frame.Stride >> 1
|
||||
}
|
||||
return &processor
|
||||
}
|
||||
|
||||
// Process converts RGBA colorspace into YUV I420 format inside the internal buffer.
|
||||
// Non-threaded version.
|
||||
func (yuv *processor) Process(rgba *image.RGBA) []byte {
|
||||
buf := *yuv.pool.Get().(*[]byte)
|
||||
C.rgbaToYuv(unsafe.Pointer(&buf[0]), unsafe.Pointer(&rgba.Pix[0]), yuv.ww, C.int(yuv.h))
|
||||
buf := *c.pool.Get().(*[]byte)
|
||||
libyuv.Y420(frame.Data, buf, frame.W, frame.H, stride, dx, dy, rot, uint32(pf), cx, cy)
|
||||
|
||||
if c.scale > 1 {
|
||||
dstBuf := *c.pool.Get().(*[]byte)
|
||||
libyuv.Y420Scale(buf, dstBuf, dx, dy, c.sw, c.sh)
|
||||
c.pool.Put(&buf)
|
||||
return dstBuf
|
||||
}
|
||||
return buf
|
||||
}
|
||||
|
||||
func (yuv *processor) Put(x *[]byte) { yuv.pool.Put(x) }
|
||||
func (c *Conv) Put(x *[]byte) { c.pool.Put(x) }
|
||||
func (c *Conv) Version() string { return libyuv.Version() }
|
||||
func round(x int, scale float64) int { return (int(float64(x)*scale) + 1) & ^1 }
|
||||
|
||||
// Process converts RGBA colorspace into YUV I420 format inside the internal buffer.
|
||||
// Threaded version.
|
||||
//
|
||||
// We divide the input image into chunks by the number of available CPUs.
|
||||
// Each chunk should contain 2, 4, 6, etc. rows of the image.
|
||||
//
|
||||
// 8x4 CPU (2)
|
||||
// x x x x x x x x | Coroutine 1
|
||||
// x x x x x x x x | Coroutine 1
|
||||
// x x x x x x x x | Coroutine 2
|
||||
// x x x x x x x x | Coroutine 2
|
||||
func (yuv *threadedProcessor) Process(rgba *image.RGBA) []byte {
|
||||
src := unsafe.Pointer(&rgba.Pix[0])
|
||||
buf := *yuv.pool.Get().(*[]byte)
|
||||
dst := unsafe.Pointer(&buf[0])
|
||||
yuv.wg.Add(yuv.threads << 1)
|
||||
chunk := yuv.w * yuv.chunk
|
||||
for i := 0; i < yuv.threads; i++ {
|
||||
pos, hh := C.int(i*chunk), C.int(yuv.chunk)
|
||||
if i == yuv.threads-1 {
|
||||
hh = C.int(yuv.h - i*yuv.chunk)
|
||||
}
|
||||
go yuv.chroma_(src, dst, pos, hh)
|
||||
go yuv.luma_(src, dst, pos, hh)
|
||||
}
|
||||
yuv.wg.Wait()
|
||||
return buf
|
||||
}
|
||||
func ToYCbCr(bytes []byte, w, h int) *image.YCbCr {
|
||||
cw, ch := (w+1)/2, (h+1)/2
|
||||
|
||||
func (yuv *threadedProcessor) luma_(src unsafe.Pointer, dst unsafe.Pointer, pos C.int, hh C.int) {
|
||||
C.luma(dst, src, pos, yuv.ww, hh)
|
||||
yuv.wg.Done()
|
||||
}
|
||||
i0 := w*h + 0*cw*ch
|
||||
i1 := w*h + 1*cw*ch
|
||||
i2 := w*h + 2*cw*ch
|
||||
|
||||
func (yuv *threadedProcessor) chroma_(src unsafe.Pointer, dst unsafe.Pointer, pos C.int, hh C.int) {
|
||||
C.chroma(dst, src, pos, yuv.chromaU, yuv.chromaV, yuv.ww, hh)
|
||||
yuv.wg.Done()
|
||||
yuv := image.NewYCbCr(image.Rect(0, 0, w, h), image.YCbCrSubsampleRatio420)
|
||||
yuv.Y = bytes[:i0:i0]
|
||||
yuv.Cb = bytes[i0:i1:i1]
|
||||
yuv.Cr = bytes[i1:i2:i2]
|
||||
return yuv
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +0,0 @@
|
|||
#ifndef YUV_H__
|
||||
#define YUV_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
// Converts RGBA image to YUV (I420) with BT.601 studio color range.
|
||||
void rgbaToYuv(void *destination, void *source, int width, int height);
|
||||
|
||||
// Converts RGBA image chunk to YUV (I420) chroma with BT.601 studio color range.
|
||||
// pos contains a shift value for chunks.
|
||||
// deu, dev contains constant shifts for U, V planes in the resulting array.
|
||||
// chroma (0, 1) selects chroma estimation algorithm.
|
||||
void chroma(void *destination, void *source, int pos, int deu, int dev, int width, int height);
|
||||
|
||||
// Converts RGBA image chunk to YUV (I420) luma with BT.601 studio color range.
|
||||
void luma(void *destination, void *source, int pos, int width, int height);
|
||||
|
||||
#endif
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -1,7 +1,5 @@
|
|||
package app
|
||||
|
||||
import "image"
|
||||
|
||||
type App interface {
|
||||
AudioSampleRate() int
|
||||
Init() error
|
||||
|
|
@ -20,6 +18,12 @@ type Audio struct {
|
|||
}
|
||||
|
||||
type Video struct {
|
||||
Frame image.RGBA
|
||||
Frame RawFrame
|
||||
Duration int32
|
||||
}
|
||||
|
||||
type RawFrame struct {
|
||||
Data []byte
|
||||
Stride int
|
||||
W, H int
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,8 +50,7 @@ func (c *Caged) Load(game games.GameMetadata, path string) error {
|
|||
return err
|
||||
}
|
||||
w, h := c.ViewportCalc()
|
||||
c.SetViewport(w, h, c.conf.Emulator.Scale)
|
||||
|
||||
c.SetViewport(w, h)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -75,8 +74,11 @@ func (c *Caged) EnableCloudStorage(uid string, storage cloud.Storage) {
|
|||
}
|
||||
}
|
||||
|
||||
func (c *Caged) PixFormat() uint32 { return c.Emulator.PixFormat() }
|
||||
func (c *Caged) Rotation() uint { return c.Emulator.Rotation() }
|
||||
func (c *Caged) AudioSampleRate() int { return c.Emulator.AudioSampleRate() }
|
||||
func (c *Caged) ViewportSize() (int, int) { return c.Emulator.ViewportSize() }
|
||||
func (c *Caged) Scale() float64 { return c.Emulator.Scale() }
|
||||
func (c *Caged) SendControl(port int, data []byte) { c.base.Input(port, data) }
|
||||
func (c *Caged) Start() { go c.Emulator.Start() }
|
||||
func (c *Caged) SetSaveOnClose(v bool) { c.base.SaveOnClose = v }
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import (
|
|||
"github.com/giongto35/cloud-game/v3/pkg/logger"
|
||||
"github.com/giongto35/cloud-game/v3/pkg/os"
|
||||
"github.com/giongto35/cloud-game/v3/pkg/worker/caged/app"
|
||||
"github.com/giongto35/cloud-game/v3/pkg/worker/caged/libretro/image"
|
||||
"github.com/giongto35/cloud-game/v3/pkg/worker/caged/libretro/nanoarch"
|
||||
)
|
||||
|
||||
|
|
@ -25,12 +24,14 @@ type Emulator interface {
|
|||
LoadGame(path string) error
|
||||
FPS() int
|
||||
Flipped() bool
|
||||
Rotation() uint
|
||||
PixFormat() uint32
|
||||
AudioSampleRate() int
|
||||
IsPortrait() bool
|
||||
// Start is called after LoadGame
|
||||
Start()
|
||||
// SetViewport sets viewport size
|
||||
SetViewport(width int, height int, scale int)
|
||||
SetViewport(width int, height int)
|
||||
// ViewportCalc calculates the viewport size with the aspect ratio and scale
|
||||
ViewportCalc() (nw int, nh int)
|
||||
ViewportSize() (w, h int)
|
||||
|
|
@ -48,10 +49,11 @@ type Emulator interface {
|
|||
ToggleMultitap()
|
||||
// Input passes input to the emulator
|
||||
Input(player int, data []byte)
|
||||
// Scale returns set video scale factor
|
||||
Scale() float64
|
||||
}
|
||||
|
||||
type Frontend struct {
|
||||
canvas *image.Canvas
|
||||
conf config.Emulator
|
||||
done chan struct{}
|
||||
input InputState
|
||||
|
|
@ -60,6 +62,7 @@ type Frontend struct {
|
|||
onAudio func(app.Audio)
|
||||
onVideo func(app.Video)
|
||||
storage Storage
|
||||
scale float64
|
||||
th int // draw threads
|
||||
vw, vh int // out frame size
|
||||
|
||||
|
|
@ -151,6 +154,12 @@ func (f *Frontend) LoadCore(emu string) {
|
|||
UsesLibCo: conf.UsesLibCo,
|
||||
}
|
||||
f.mu.Lock()
|
||||
scale := 1.0
|
||||
if conf.Scale > 1 {
|
||||
scale = conf.Scale
|
||||
f.log.Debug().Msgf("Scale: x%v", scale)
|
||||
}
|
||||
f.scale = scale
|
||||
f.nano.CoreLoad(meta)
|
||||
f.mu.Unlock()
|
||||
}
|
||||
|
|
@ -169,30 +178,27 @@ func (f *Frontend) handleAudio(audio unsafe.Pointer, samples int) {
|
|||
}
|
||||
|
||||
func (f *Frontend) handleVideo(data []byte, delta int32, fi nanoarch.FrameInfo) {
|
||||
pixFmt := f.nano.Video.PixFmt
|
||||
bpp := int(f.nano.Video.BPP)
|
||||
drawn := f.canvas.Draw(pixFmt, f.nano.Rot, int(fi.W), int(fi.H), int(fi.Packed), bpp, data, f.th)
|
||||
|
||||
// !to merge both pools
|
||||
fr, _ := videoPool.Get().(*app.Video)
|
||||
if fr == nil {
|
||||
fr = new(app.Video)
|
||||
}
|
||||
fr.Frame = drawn.Unwrap()
|
||||
fr.Frame.Data = data
|
||||
fr.Frame.W = int(fi.W)
|
||||
fr.Frame.H = int(fi.H)
|
||||
fr.Frame.Stride = int(fi.Stride)
|
||||
fr.Duration = delta
|
||||
f.onVideo(*fr)
|
||||
f.canvas.Put(drawn)
|
||||
videoPool.Put(fr)
|
||||
}
|
||||
|
||||
func (f *Frontend) Shutdown() {
|
||||
f.log.Debug().Msgf("run loop cleanup")
|
||||
f.mu.Lock()
|
||||
f.nano.Shutdown()
|
||||
f.canvas.Clear()
|
||||
f.SetAudioCb(noAudio)
|
||||
f.SetVideoCb(noVideo)
|
||||
f.mu.Unlock()
|
||||
f.log.Debug().Msgf("run loop finished")
|
||||
f.log.Debug().Msgf("frontend closed")
|
||||
}
|
||||
|
||||
func (f *Frontend) linkNano(nano *nanoarch.Nanoarch) {
|
||||
|
|
@ -240,6 +246,8 @@ func (f *Frontend) Start() {
|
|||
}
|
||||
}
|
||||
|
||||
func (f *Frontend) PixFormat() uint32 { return f.nano.Video.PixFmt.C }
|
||||
func (f *Frontend) Rotation() uint { return f.nano.Rot }
|
||||
func (f *Frontend) Flipped() bool { return f.nano.IsGL() }
|
||||
func (f *Frontend) FrameSize() (int, int) { return f.nano.GeometryBase() }
|
||||
func (f *Frontend) FPS() int { return f.nano.VideoFramerate() }
|
||||
|
|
@ -250,21 +258,15 @@ func (f *Frontend) AudioSampleRate() int { return f.nano.AudioSampleRat
|
|||
func (f *Frontend) Input(player int, data []byte) { f.input.setInput(player, data) }
|
||||
func (f *Frontend) LoadGame(path string) error { return f.nano.LoadGame(path) }
|
||||
func (f *Frontend) RestoreGameState() error { return f.Load() }
|
||||
func (f *Frontend) Scale() float64 { return f.scale }
|
||||
func (f *Frontend) IsPortrait() bool { return f.nano.IsPortrait() }
|
||||
func (f *Frontend) SaveGameState() error { return f.Save() }
|
||||
func (f *Frontend) Scale(factor int) { w, h := f.ViewportSize(); f.SetViewport(w, h, factor) }
|
||||
func (f *Frontend) SetAudioCb(cb func(app.Audio)) { f.onAudio = cb }
|
||||
func (f *Frontend) SetSessionId(name string) { f.storage.SetMainSaveName(name) }
|
||||
func (f *Frontend) SetVideoCb(ff func(app.Video)) { f.onVideo = ff }
|
||||
func (f *Frontend) SetViewport(width int, height int, scale int) {
|
||||
func (f *Frontend) SetViewport(width int, height int) {
|
||||
f.mu.Lock()
|
||||
f.vw, f.vh = width, height
|
||||
mw, mh := f.nano.GeometryMax()
|
||||
size := mw * scale * mh * scale
|
||||
f.canvas = image.NewCanvas(width, height, size)
|
||||
if f.DisableCanvasPool {
|
||||
f.canvas.SetEnabled(false)
|
||||
}
|
||||
f.mu.Unlock()
|
||||
}
|
||||
|
||||
|
|
@ -292,14 +294,9 @@ func (f *Frontend) ViewportCalc() (nw int, nh int) {
|
|||
nw, nh = w, h
|
||||
}
|
||||
|
||||
if f.conf.Scale > 1 {
|
||||
nw, nh = nw*f.conf.Scale, nh*f.conf.Scale
|
||||
f.log.Debug().Msgf("Viewport size scaled: %dx%d", nw, nh)
|
||||
}
|
||||
|
||||
if f.IsPortrait() {
|
||||
nw, nh = nh, nw
|
||||
f.log.Debug().Msgf("Viewport was flipped")
|
||||
f.log.Debug().Msgf("Set portrait mode")
|
||||
}
|
||||
|
||||
f.log.Info().Msgf("Viewport final size: %dx%d", nw, nh)
|
||||
|
|
|
|||
|
|
@ -7,18 +7,27 @@ import (
|
|||
"log"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"testing"
|
||||
"unsafe"
|
||||
|
||||
"github.com/giongto35/cloud-game/v3/pkg/config"
|
||||
"github.com/giongto35/cloud-game/v3/pkg/logger"
|
||||
"github.com/giongto35/cloud-game/v3/pkg/worker/caged/app"
|
||||
"github.com/giongto35/cloud-game/v3/pkg/worker/caged/libretro/manager"
|
||||
"github.com/giongto35/cloud-game/v3/pkg/worker/caged/libretro/nanoarch"
|
||||
"github.com/giongto35/cloud-game/v3/pkg/worker/thread"
|
||||
|
||||
_ "github.com/giongto35/cloud-game/v3/test"
|
||||
)
|
||||
|
||||
type TestFrontend struct {
|
||||
*Frontend
|
||||
|
||||
corePath string
|
||||
gamePath string
|
||||
}
|
||||
|
||||
type testRun struct {
|
||||
room string
|
||||
system string
|
||||
|
|
@ -26,46 +35,48 @@ type testRun struct {
|
|||
emulationTicks int
|
||||
}
|
||||
|
||||
// EmulatorMock contains Frontend mocking data.
|
||||
type EmulatorMock struct {
|
||||
*Frontend
|
||||
|
||||
// Libretro compiled lib core name
|
||||
core string
|
||||
// shared core paths (can't be changed)
|
||||
paths EmulatorPaths
|
||||
type game struct {
|
||||
rom string
|
||||
system string
|
||||
}
|
||||
|
||||
// EmulatorPaths defines various emulator file paths.
|
||||
type EmulatorPaths struct {
|
||||
assets string
|
||||
cores string
|
||||
games string
|
||||
save string
|
||||
var (
|
||||
alwa = game{system: "nes", rom: "Alwa's Awakening (Demo).nes"}
|
||||
sushi = game{system: "gba", rom: "Sushi The Cat.gba"}
|
||||
angua = game{system: "gba", rom: "anguna.gba"}
|
||||
)
|
||||
|
||||
// TestMain runs all tests in the main thread in macOS.
|
||||
func TestMain(m *testing.M) {
|
||||
thread.Wrap(func() { os.Exit(m.Run()) })
|
||||
}
|
||||
|
||||
// GetEmulatorMock returns a properly stubbed emulator instance.
|
||||
// EmulatorMock returns a properly stubbed emulator instance.
|
||||
// Due to extensive use of globals -- one mock instance is allowed per a test run.
|
||||
// Don't forget to init one image channel consumer, it will lock-out otherwise.
|
||||
// Make sure you call Shutdown().
|
||||
func GetEmulatorMock(room string, system string) *EmulatorMock {
|
||||
rootPath := getRootPath()
|
||||
|
||||
func EmulatorMock(room string, system string) *TestFrontend {
|
||||
var conf config.WorkerConfig
|
||||
if _, err := config.LoadConfig(&conf, ""); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
meta := conf.Emulator.GetLibretroCoreConfig(system)
|
||||
|
||||
nano := nanoarch.NewNano(cleanPath(conf.Emulator.LocalPath))
|
||||
conf.Emulator.Libretro.Cores.Repo.ExtLock = expand("tests", ".cr", "cloud-game.lock")
|
||||
conf.Emulator.LocalPath = expand("tests", conf.Emulator.LocalPath)
|
||||
conf.Emulator.Storage = expand("tests", "storage")
|
||||
|
||||
l := logger.Default()
|
||||
l2 := l.Extend(l.Level(logger.ErrorLevel).With())
|
||||
|
||||
if err := manager.CheckCores(conf.Emulator, l); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
nano := nanoarch.NewNano(conf.Emulator.LocalPath)
|
||||
nano.SetLogger(l2)
|
||||
|
||||
// an emu
|
||||
emu := &EmulatorMock{
|
||||
emu := &TestFrontend{
|
||||
Frontend: &Frontend{
|
||||
conf: conf.Emulator,
|
||||
storage: &StateStorage{
|
||||
|
|
@ -78,27 +89,19 @@ func GetEmulatorMock(room string, system string) *EmulatorMock {
|
|||
log: l2,
|
||||
SaveOnClose: false,
|
||||
},
|
||||
|
||||
core: path.Base(meta.Lib),
|
||||
|
||||
paths: EmulatorPaths{
|
||||
assets: cleanPath(rootPath),
|
||||
cores: cleanPath(rootPath + "assets/cores/"),
|
||||
games: cleanPath(rootPath + "assets/games/"),
|
||||
},
|
||||
corePath: expand(conf.Emulator.GetLibretroCoreConfig(system).Lib),
|
||||
gamePath: expand(conf.Worker.Library.BasePath),
|
||||
}
|
||||
emu.linkNano(nano)
|
||||
|
||||
emu.paths.save = cleanPath(emu.HashPath())
|
||||
|
||||
return emu
|
||||
}
|
||||
|
||||
// GetDefaultFrontend returns initialized emulator mock with default params.
|
||||
// DefaultFrontend returns initialized emulator mock with default params.
|
||||
// Spawns audio/image channels consumers.
|
||||
// Don't forget to close emulator mock with Shutdown().
|
||||
func GetDefaultFrontend(room string, system string, rom string) *EmulatorMock {
|
||||
mock := GetEmulatorMock(room, system)
|
||||
func DefaultFrontend(room string, system string, rom string) *TestFrontend {
|
||||
mock := EmulatorMock(room, system)
|
||||
mock.loadRom(rom)
|
||||
mock.SetVideoCb(func(app.Video) {})
|
||||
mock.SetAudioCb(func(app.Audio) {})
|
||||
|
|
@ -107,25 +110,30 @@ func GetDefaultFrontend(room string, system string, rom string) *EmulatorMock {
|
|||
|
||||
// loadRom loads a ROM into the emulator.
|
||||
// The rom will be loaded from emulators' games path.
|
||||
func (emu *EmulatorMock) loadRom(game string) {
|
||||
fmt.Printf("%v %v\n", emu.paths.cores, emu.core)
|
||||
emu.nano.CoreLoad(nanoarch.Metadata{LibPath: emu.paths.cores + emu.core})
|
||||
err := emu.nano.LoadGame(emu.paths.games + game)
|
||||
func (emu *TestFrontend) loadRom(game string) {
|
||||
emu.nano.CoreLoad(nanoarch.Metadata{LibPath: emu.corePath})
|
||||
|
||||
gamePath := expand(emu.gamePath, game)
|
||||
|
||||
conf := emu.conf.GetLibretroCoreConfig(gamePath)
|
||||
scale := 1.0
|
||||
if conf.Scale > 1 {
|
||||
scale = conf.Scale
|
||||
}
|
||||
emu.scale = scale
|
||||
|
||||
err := emu.nano.LoadGame(gamePath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
w, h := emu.FrameSize()
|
||||
if emu.conf.Scale == 0 {
|
||||
emu.conf.Scale = 1
|
||||
}
|
||||
emu.SetViewport(w, h, emu.conf.Scale)
|
||||
emu.SetViewport(w, h)
|
||||
}
|
||||
|
||||
// Shutdown closes the emulator and cleans its resources.
|
||||
func (emu *EmulatorMock) Shutdown() {
|
||||
func (emu *TestFrontend) Shutdown() {
|
||||
_ = os.Remove(emu.HashPath())
|
||||
_ = os.Remove(emu.SRAMPath())
|
||||
|
||||
emu.Frontend.Close()
|
||||
emu.Frontend.Shutdown()
|
||||
}
|
||||
|
|
@ -133,97 +141,56 @@ func (emu *EmulatorMock) Shutdown() {
|
|||
// dumpState returns the current emulator state and
|
||||
// the latest saved state for its session.
|
||||
// Locks the emulator.
|
||||
func (emu *EmulatorMock) dumpState() (string, string) {
|
||||
func (emu *TestFrontend) dumpState() (string, string) {
|
||||
emu.mu.Lock()
|
||||
bytes, _ := os.ReadFile(emu.paths.save)
|
||||
persistedStateHash := getHash(bytes)
|
||||
bytes, _ := os.ReadFile(emu.HashPath())
|
||||
lastStateHash := hash(bytes)
|
||||
emu.mu.Unlock()
|
||||
|
||||
stateHash := emu.getStateHash()
|
||||
fmt.Printf("mem: %v, dat: %v\n", stateHash, persistedStateHash)
|
||||
return stateHash, persistedStateHash
|
||||
}
|
||||
|
||||
// getStateHash returns the current emulator state hash.
|
||||
// Locks the emulator.
|
||||
func (emu *EmulatorMock) getStateHash() string {
|
||||
emu.mu.Lock()
|
||||
state, _ := nanoarch.SaveState()
|
||||
emu.mu.Unlock()
|
||||
stateHash := hash(state)
|
||||
|
||||
return getHash(state)
|
||||
fmt.Printf("mem: %v, dat: %v\n", stateHash, lastStateHash)
|
||||
return stateHash, lastStateHash
|
||||
}
|
||||
|
||||
// getRootPath returns absolute path to the root directory.
|
||||
func getRootPath() string {
|
||||
p, _ := filepath.Abs("../../../../")
|
||||
return p + string(filepath.Separator)
|
||||
}
|
||||
|
||||
// getHash returns MD5 hash.
|
||||
func getHash(bytes []byte) string { return fmt.Sprintf("%x", md5.Sum(bytes)) }
|
||||
|
||||
// cleanPath returns a proper file path for current OS.
|
||||
func cleanPath(path string) string { return filepath.FromSlash(path) }
|
||||
|
||||
// benchmarkEmulator is a generic function for
|
||||
// measuring emulator performance for one emulation frame.
|
||||
func benchmarkEmulator(system string, rom string, b *testing.B) {
|
||||
b.StopTimer()
|
||||
func BenchmarkEmulators(b *testing.B) {
|
||||
log.SetOutput(io.Discard)
|
||||
os.Stdout, _ = os.Open(os.DevNull)
|
||||
|
||||
s := GetDefaultFrontend("bench_"+system+"_performance", system, rom)
|
||||
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
s.nano.Run()
|
||||
benchmarks := []struct {
|
||||
name string
|
||||
system string
|
||||
rom string
|
||||
}{
|
||||
{name: "GBA Sushi", system: sushi.system, rom: sushi.rom},
|
||||
{name: "NES Alwa", system: alwa.system, rom: alwa.rom},
|
||||
}
|
||||
s.Shutdown()
|
||||
}
|
||||
|
||||
func BenchmarkEmulatorGba(b *testing.B) {
|
||||
benchmarkEmulator("gba", "Sushi The Cat.gba", b)
|
||||
}
|
||||
|
||||
func BenchmarkEmulatorNes(b *testing.B) {
|
||||
benchmarkEmulator("nes", "Alwa's Awakening (Demo).nes", b)
|
||||
}
|
||||
|
||||
func TestSwap(t *testing.T) {
|
||||
data := []byte{1, 254, 255, 32}
|
||||
pixel := *(*uint32)(unsafe.Pointer(&data[0]))
|
||||
// 0 1 2 3
|
||||
// 2 1 0 3
|
||||
ll := ((pixel >> 16) & 0xff) | (pixel & 0xff00) | ((pixel << 16) & 0xff0000) | 0xff000000
|
||||
|
||||
rez := []byte{0, 0, 0, 0}
|
||||
*(*uint32)(unsafe.Pointer(&rez[0])) = ll
|
||||
|
||||
log.Printf("%v\n%v", data, rez)
|
||||
for _, bench := range benchmarks {
|
||||
b.Run(bench.name, func(b *testing.B) {
|
||||
s := DefaultFrontend("bench_"+bench.system+"_performance", bench.system, bench.rom)
|
||||
for i := 0; i < b.N; i++ {
|
||||
s.nano.Run()
|
||||
}
|
||||
s.Shutdown()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Tests a successful emulator state save.
|
||||
func TestSave(t *testing.T) {
|
||||
tests := []testRun{
|
||||
{
|
||||
room: "test_save_ok_00",
|
||||
system: "gba",
|
||||
rom: "Sushi The Cat.gba",
|
||||
emulationTicks: 100,
|
||||
},
|
||||
{
|
||||
room: "test_save_ok_01",
|
||||
system: "gba",
|
||||
rom: "anguna.gba",
|
||||
emulationTicks: 10,
|
||||
},
|
||||
{room: "test_save_ok_00", system: sushi.system, rom: sushi.rom, emulationTicks: 100},
|
||||
{room: "test_save_ok_01", system: angua.system, rom: angua.rom, emulationTicks: 10},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Logf("Testing [%v] save with [%v]\n", test.system, test.rom)
|
||||
|
||||
front := GetDefaultFrontend(test.room, test.system, test.rom)
|
||||
front := DefaultFrontend(test.room, test.system, test.rom)
|
||||
|
||||
for test.emulationTicks > 0 {
|
||||
front.Tick()
|
||||
|
|
@ -255,30 +222,15 @@ func TestSave(t *testing.T) {
|
|||
// Compare states (a) and (b), should be =.
|
||||
func TestLoad(t *testing.T) {
|
||||
tests := []testRun{
|
||||
{
|
||||
room: "test_load_00",
|
||||
system: "nes",
|
||||
rom: "Alwa's Awakening (Demo).nes",
|
||||
emulationTicks: 100,
|
||||
},
|
||||
{
|
||||
room: "test_load_01",
|
||||
system: "gba",
|
||||
rom: "Sushi The Cat.gba",
|
||||
emulationTicks: 1000,
|
||||
},
|
||||
{
|
||||
room: "test_load_02",
|
||||
system: "gba",
|
||||
rom: "anguna.gba",
|
||||
emulationTicks: 100,
|
||||
},
|
||||
{room: "test_load_00", system: alwa.system, rom: alwa.rom, emulationTicks: 100},
|
||||
{room: "test_load_01", system: sushi.system, rom: sushi.rom, emulationTicks: 1000},
|
||||
{room: "test_load_02", system: angua.system, rom: angua.rom, emulationTicks: 100},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Logf("Testing [%v] load with [%v]\n", test.system, test.rom)
|
||||
|
||||
mock := GetDefaultFrontend(test.room, test.system, test.rom)
|
||||
mock := DefaultFrontend(test.room, test.system, test.rom)
|
||||
|
||||
fmt.Printf("[%-14v] ", "initial")
|
||||
mock.dumpState()
|
||||
|
|
@ -317,26 +269,15 @@ func TestLoad(t *testing.T) {
|
|||
|
||||
func TestStateConcurrency(t *testing.T) {
|
||||
tests := []struct {
|
||||
run testRun
|
||||
// determine random
|
||||
run testRun
|
||||
seed int
|
||||
}{
|
||||
{
|
||||
run: testRun{
|
||||
room: "test_concurrency_00",
|
||||
system: "gba",
|
||||
rom: "Sushi The Cat.gba",
|
||||
emulationTicks: 120,
|
||||
},
|
||||
run: testRun{room: "test_concurrency_00", system: sushi.system, rom: sushi.rom, emulationTicks: 120},
|
||||
seed: 42,
|
||||
},
|
||||
{
|
||||
run: testRun{
|
||||
room: "test_concurrency_01",
|
||||
system: "gba",
|
||||
rom: "anguna.gba",
|
||||
emulationTicks: 300,
|
||||
},
|
||||
run: testRun{room: "test_concurrency_01", system: angua.system, rom: angua.rom, emulationTicks: 300},
|
||||
seed: 42 + 42,
|
||||
},
|
||||
}
|
||||
|
|
@ -344,7 +285,7 @@ func TestStateConcurrency(t *testing.T) {
|
|||
for _, test := range tests {
|
||||
t.Logf("Testing [%v] concurrency with [%v]\n", test.run.system, test.run.rom)
|
||||
|
||||
mock := GetEmulatorMock(test.run.room, test.run.system)
|
||||
mock := EmulatorMock(test.run.room, test.run.system)
|
||||
|
||||
ops := &sync.WaitGroup{}
|
||||
// quantum lock
|
||||
|
|
@ -352,14 +293,14 @@ func TestStateConcurrency(t *testing.T) {
|
|||
|
||||
mock.loadRom(test.run.rom)
|
||||
mock.SetVideoCb(func(v app.Video) {
|
||||
if len(v.Frame.Pix) == 0 {
|
||||
if len(v.Frame.Data) == 0 {
|
||||
t.Errorf("It seems that rom video frame was empty, which is strange!")
|
||||
}
|
||||
})
|
||||
mock.SetAudioCb(func(app.Audio) {})
|
||||
|
||||
t.Logf("Random seed is [%v]\n", test.seed)
|
||||
t.Logf("Save path is [%v]\n", mock.paths.save)
|
||||
t.Logf("Save path is [%v]\n", mock.HashPath())
|
||||
|
||||
_ = mock.Save()
|
||||
|
||||
|
|
@ -404,36 +345,28 @@ func TestStateConcurrency(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// lucky returns random boolean.
|
||||
func lucky() bool { return rand.Intn(2) == 1 }
|
||||
|
||||
func TestConcurrentInput(t *testing.T) {
|
||||
players := NewGameSessionInput()
|
||||
|
||||
events := 1000
|
||||
var wg sync.WaitGroup
|
||||
state := NewGameSessionInput()
|
||||
events := 1000
|
||||
wg.Add(2 * events)
|
||||
|
||||
wg.Add(events * 2)
|
||||
|
||||
go func() {
|
||||
for i := 0; i < events; i++ {
|
||||
player := rand.Intn(maxPort)
|
||||
go func() {
|
||||
players.setInput(player, []byte{0, 1})
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
for i := 0; i < events; i++ {
|
||||
player := rand.Intn(maxPort)
|
||||
go func() {
|
||||
players.isKeyPressed(uint(player), 100)
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
}()
|
||||
|
||||
for i := 0; i < events; i++ {
|
||||
player := rand.Intn(maxPort)
|
||||
go func() { state.setInput(player, []byte{0, 1}); wg.Done() }()
|
||||
go func() { state.isKeyPressed(uint(player), 100); wg.Done() }()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// expand joins a list of file path elements.
|
||||
func expand(p ...string) string {
|
||||
ph, _ := filepath.Abs(filepath.FromSlash(filepath.Join(p...)))
|
||||
return ph
|
||||
}
|
||||
|
||||
// hash returns MD5 hash.
|
||||
func hash(bytes []byte) string { return fmt.Sprintf("%x", md5.Sum(bytes)) }
|
||||
|
||||
// lucky returns random boolean.
|
||||
func lucky() bool { return rand.Intn(2) == 1 }
|
||||
|
|
|
|||
|
|
@ -1,88 +0,0 @@
|
|||
#include "canvas.h"
|
||||
|
||||
__inline xy rotate(int t, int x, int y, int w, int h) {
|
||||
xy p = {x, y};
|
||||
switch (t) {
|
||||
// 90° CCW or 270° CW
|
||||
case A90:
|
||||
p.x = y;
|
||||
p.y = w - 1 - x;
|
||||
break;
|
||||
// 180° CCW
|
||||
case A180:
|
||||
p.x = w - 1 - x;
|
||||
p.y = h - 1 - y;
|
||||
break;
|
||||
// 270° CCW or 90° CW
|
||||
case A270:
|
||||
p.x = h - 1 - y;
|
||||
p.y = x;
|
||||
break;
|
||||
// flip Y
|
||||
case F180:
|
||||
//p.x = x;
|
||||
p.y = h - 1 - y;
|
||||
break;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
__inline uint32_t _565(uint32_t x) {
|
||||
return ((x >> 8 & 0xf8) | ((x >> 3 & 0xfc) << 8) | ((x << 3 & 0xfc) << 16)); // | 0xff000000
|
||||
}
|
||||
|
||||
__inline uint32_t _8888rev(uint32_t px) {
|
||||
return (((px >> 16) & 0xff) | (px & 0xff00) | ((px << 16) & 0xff0000)); // | 0xff000000
|
||||
}
|
||||
|
||||
void RGBA(int pix, uint32_t *__restrict dst, const void *__restrict source, int y, int h, int w, int hh, int dw, int pad, int rot) {
|
||||
int x;
|
||||
xy rxy;
|
||||
const uint16_t *src16;
|
||||
const uint32_t *src32;
|
||||
|
||||
switch (pix) {
|
||||
//case BIT_SHORT5551:
|
||||
// break;
|
||||
case BIT_INT_8888REV:
|
||||
src32 = (const uint32_t *)source;
|
||||
int pad32 = pad >> 2;
|
||||
if (rot == NO_ROT) {
|
||||
for (; y < h; ++y) {
|
||||
for (x = 0; x < w; ++x) {
|
||||
*dst++ = _8888rev(*src32++);
|
||||
}
|
||||
src32 += pad32;
|
||||
}
|
||||
} else {
|
||||
for (; y < h; ++y) {
|
||||
for (x = 0; x < w; ++x) {
|
||||
rxy = rotate(rot, x, y, w, hh);
|
||||
dst[rxy.x+rxy.y*dw] = _8888rev(*src32++);
|
||||
}
|
||||
src32 += pad32;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case BIT_SHORT565:
|
||||
src16 = (const uint16_t *)source;
|
||||
int pad16 = pad >> 1;
|
||||
if (rot == NO_ROT) {
|
||||
for (; y < h; ++y) {
|
||||
for (x = 0; x < w; ++x) {
|
||||
*dst++ = _565(*src16++);
|
||||
}
|
||||
src16 += pad16;
|
||||
}
|
||||
} else {
|
||||
for (; y < h; ++y) {
|
||||
for (x = 0; x < w; ++x) {
|
||||
rxy = rotate(rot, x, y, w, hh);
|
||||
dst[rxy.x+rxy.y*dw] = _565(*src16++);
|
||||
}
|
||||
src16 += pad16;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,159 +0,0 @@
|
|||
package image
|
||||
|
||||
import (
|
||||
"image"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/image/draw"
|
||||
)
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -Wall
|
||||
#include "canvas.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
// Canvas is a stateful drawing surface, i.e. image.RGBA
|
||||
type Canvas struct {
|
||||
enabled bool
|
||||
w, h int
|
||||
vertical bool
|
||||
pool sync.Pool
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
type Frame struct{ image.RGBA }
|
||||
|
||||
func (f *Frame) Unwrap() image.RGBA { return f.RGBA }
|
||||
func (f *Frame) Opaque() bool { return true }
|
||||
func (f *Frame) Copy() Frame {
|
||||
return Frame{image.RGBA{Pix: append([]uint8{}, f.Pix...), Stride: f.Stride, Rect: f.Rect}}
|
||||
}
|
||||
|
||||
const (
|
||||
BitFormatShort5551 = iota // BIT_FORMAT_SHORT_5_5_5_1 has 5 bits R, 5 bits G, 5 bits B, 1 bit alpha
|
||||
BitFormatInt8888Rev // BIT_FORMAT_INT_8_8_8_8_REV has 8 bits R, 8 bits G, 8 bits B, 8 bit alpha
|
||||
BitFormatShort565 // BIT_FORMAT_SHORT_5_6_5 has 5 bits R, 6 bits G, 5 bits
|
||||
)
|
||||
|
||||
const (
|
||||
ScaleNot = iota // skips image interpolation
|
||||
ScaleNearestNeighbour // nearest neighbour interpolation
|
||||
ScaleBilinear // bilinear interpolation
|
||||
)
|
||||
|
||||
func Resize(scaleType int, src *image.RGBA, out *image.RGBA) {
|
||||
// !to do set it once instead switching on each iteration
|
||||
switch scaleType {
|
||||
case ScaleBilinear:
|
||||
draw.ApproxBiLinear.Scale(out, out.Bounds(), src, src.Bounds(), draw.Src, nil)
|
||||
case ScaleNot:
|
||||
fallthrough
|
||||
case ScaleNearestNeighbour:
|
||||
fallthrough
|
||||
default:
|
||||
draw.NearestNeighbor.Scale(out, out.Bounds(), src, src.Bounds(), draw.Src, nil)
|
||||
}
|
||||
}
|
||||
|
||||
type Rotation uint
|
||||
|
||||
const (
|
||||
A90 Rotation = iota + 1
|
||||
A180
|
||||
A270
|
||||
F180 // F180 is flipped Y
|
||||
)
|
||||
|
||||
func NewCanvas(w, h, size int) *Canvas {
|
||||
return &Canvas{
|
||||
enabled: true,
|
||||
w: w,
|
||||
h: h,
|
||||
vertical: h > w, // input is inverted
|
||||
pool: sync.Pool{New: func() any {
|
||||
i := Frame{image.RGBA{
|
||||
Pix: make([]uint8, size<<2),
|
||||
Rect: image.Rectangle{Max: image.Point{X: w, Y: h}},
|
||||
}}
|
||||
return &i
|
||||
}},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Canvas) Get(w, h int) *Frame {
|
||||
i := c.pool.Get().(*Frame)
|
||||
if c.vertical {
|
||||
w, h = h, w
|
||||
}
|
||||
i.Stride = w << 2
|
||||
i.Pix = i.Pix[:i.Stride*h]
|
||||
i.Rect.Max.X = w
|
||||
i.Rect.Max.Y = h
|
||||
return i
|
||||
}
|
||||
|
||||
func (c *Canvas) Put(i *Frame) {
|
||||
if c.enabled {
|
||||
c.pool.Put(i)
|
||||
}
|
||||
}
|
||||
func (c *Canvas) Clear() { c.wg = sync.WaitGroup{} }
|
||||
func (c *Canvas) SetEnabled(enabled bool) { c.enabled = enabled }
|
||||
|
||||
func (c *Canvas) Draw(encoding uint32, rot Rotation, w, h, packedW, bpp int, data []byte, th int) *Frame {
|
||||
dst := c.Get(w, h)
|
||||
if th == 0 {
|
||||
frame(encoding, dst, data, 0, h, w, h, packedW, bpp, rot)
|
||||
} else {
|
||||
hn := h / th
|
||||
c.wg.Add(th)
|
||||
for i := 0; i < th; i++ {
|
||||
xx := hn * i
|
||||
go func() {
|
||||
frame(encoding, dst, data, xx, hn, w, h, packedW, bpp, rot)
|
||||
c.wg.Done()
|
||||
}()
|
||||
}
|
||||
c.wg.Wait()
|
||||
}
|
||||
|
||||
// rescale
|
||||
if dst.Rect.Dx() != c.w || dst.Rect.Dy() != c.h {
|
||||
ww := c.w
|
||||
hh := c.h
|
||||
// w, h supposedly have been swapped before
|
||||
if c.vertical {
|
||||
ww, hh = c.h, c.w
|
||||
}
|
||||
out := c.Get(ww, hh)
|
||||
Resize(ScaleNearestNeighbour, &dst.RGBA, &out.RGBA)
|
||||
c.Put(dst)
|
||||
return out
|
||||
}
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
func frame(encoding uint32, dst *Frame, data []byte, yy int, hn int, w int, h int, pwb int, bpp int, rot Rotation) {
|
||||
sPtr := unsafe.Pointer(&data[yy*pwb])
|
||||
// some cores can zero-right-pad rows to the packed width value
|
||||
pad := pwb - w*bpp
|
||||
if pad < 0 {
|
||||
pad = 0
|
||||
}
|
||||
ds := 0
|
||||
if rot == 0 {
|
||||
ds = yy * dst.Stride
|
||||
}
|
||||
dPtr := (*C.uint32_t)(unsafe.Pointer(&dst.Pix[ds]))
|
||||
C.RGBA(C.int(encoding), dPtr, sPtr, C.int(yy), C.int(yy+hn), C.int(w), C.int(h), C.int(dst.Stride>>2), C.int(pad), C.int(rot))
|
||||
}
|
||||
|
||||
func _8888rev(px uint32) uint32 { return uint32(C._8888rev(C.uint32_t(px))) }
|
||||
|
||||
func rotate(t int, x int, y int, w int, h int) (int, int) {
|
||||
var rot C.xy = C.rotate(C.int(t), C.int(x), C.int(y), C.int(w), C.int(h))
|
||||
return int(rot.x), int(rot.y)
|
||||
}
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
#ifndef CANVAS_H__
|
||||
#define CANVAS_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define BIT_SHORT5551 0
|
||||
#define BIT_INT_8888REV 1
|
||||
#define BIT_SHORT565 2
|
||||
|
||||
#define NO_ROT 0
|
||||
#define A90 1
|
||||
#define A180 2
|
||||
#define A270 3
|
||||
#define F180 4
|
||||
|
||||
typedef struct XY {
|
||||
int x, y;
|
||||
} xy;
|
||||
|
||||
xy rotate(int t, int x, int y, int w, int h);
|
||||
|
||||
void RGBA(int pix, uint32_t *dst, const void *source, int y, int h, int w, int hh, int dw, int pad, int rot);
|
||||
|
||||
uint32_t _565(uint32_t x);
|
||||
uint32_t _8888rev(uint32_t px);
|
||||
|
||||
#endif
|
||||
|
|
@ -1,340 +0,0 @@
|
|||
package image
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func BenchmarkDraw(b *testing.B) {
|
||||
w1, h1 := 256, 240
|
||||
w2, h2 := 640, 480
|
||||
|
||||
type args struct {
|
||||
encoding uint32
|
||||
rot Rotation
|
||||
scaleType int
|
||||
w int
|
||||
h int
|
||||
packedW int
|
||||
bpp int
|
||||
data []byte
|
||||
dw int
|
||||
dh int
|
||||
th int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
}{
|
||||
{
|
||||
name: "565_0th",
|
||||
args: args{
|
||||
encoding: BitFormatShort565, scaleType: ScaleNearestNeighbour,
|
||||
w: w1, h: h1, packedW: w1, bpp: 2, data: make([]uint8, w1*h1*2), dw: w1, dh: h1, th: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "565_0th_90",
|
||||
args: args{
|
||||
encoding: BitFormatShort565, rot: A90, scaleType: ScaleNearestNeighbour,
|
||||
w: h1, h: w1, packedW: h1, bpp: 2, data: make([]uint8, w1*h1*2), dw: w1, dh: h1, th: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "565_0th",
|
||||
args: args{
|
||||
encoding: BitFormatShort565, scaleType: ScaleNearestNeighbour,
|
||||
w: w2, h: h2, packedW: w1, bpp: 2, data: make([]uint8, w2*h2*2), dw: w2, dh: h2, th: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "565_4th",
|
||||
args: args{
|
||||
encoding: BitFormatShort565, scaleType: ScaleNearestNeighbour,
|
||||
w: w1, h: h1, packedW: w1, bpp: 2, data: make([]uint8, w1*h1*2), dw: w1, dh: h1, th: 4,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "565_4th",
|
||||
args: args{
|
||||
encoding: BitFormatShort565, scaleType: ScaleNearestNeighbour,
|
||||
w: w2, h: h2, packedW: w2, bpp: 2, data: make([]uint8, w2*h2*2), dw: w2, dh: h2, th: 4,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "8888 - 0th",
|
||||
args: args{
|
||||
encoding: BitFormatInt8888Rev, scaleType: ScaleNearestNeighbour,
|
||||
w: w1, h: h1, packedW: w1, bpp: 4, data: make([]uint8, w1*h1*4), dw: w1, dh: h1, th: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "8888 - 4th",
|
||||
args: args{
|
||||
encoding: BitFormatInt8888Rev, scaleType: ScaleNearestNeighbour,
|
||||
w: w1, h: h1, packedW: w1, bpp: 4, data: make([]uint8, w1*h1*4), dw: w1, dh: h1, th: 4,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, bn := range tests {
|
||||
c := NewCanvas(bn.args.dw, bn.args.dh, bn.args.dw*bn.args.dh)
|
||||
img := c.Get(bn.args.dw, bn.args.dh)
|
||||
c.Put(img)
|
||||
img2 := c.Get(bn.args.dw, bn.args.dh)
|
||||
c.Put(img2)
|
||||
b.ResetTimer()
|
||||
b.Run(fmt.Sprintf("%vx%v_%v", bn.args.w, bn.args.h, bn.name), func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
p := c.Draw(bn.args.encoding, bn.args.rot, bn.args.w, bn.args.h, bn.args.packedW, bn.args.bpp, bn.args.data, bn.args.th)
|
||||
c.Put(p)
|
||||
}
|
||||
b.ReportAllocs()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ix8888(t *testing.T) {
|
||||
type args struct {
|
||||
dst *uint32
|
||||
px uint32
|
||||
expect uint32
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
}{
|
||||
{
|
||||
name: "",
|
||||
args: args{
|
||||
dst: new(uint32),
|
||||
px: 0x11223344,
|
||||
expect: 0x00443322,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
*tt.args.dst = _8888rev(tt.args.px)
|
||||
if *tt.args.dst != tt.args.expect {
|
||||
t.Errorf("nope, %x %x", *tt.args.dst, tt.args.expect)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type dimensions struct {
|
||||
w int
|
||||
h int
|
||||
}
|
||||
|
||||
func TestRotate(t *testing.T) {
|
||||
tests := []struct {
|
||||
// packed bytes from a 2D matrix
|
||||
input []byte
|
||||
// original matrix's width
|
||||
w int
|
||||
// original matrix's height
|
||||
h int
|
||||
// rotation algorithm
|
||||
rotateHow []Rotation
|
||||
expected [][]byte
|
||||
}{
|
||||
{
|
||||
// a cross
|
||||
[]byte{
|
||||
0, 1, 0,
|
||||
1, 1, 1,
|
||||
0, 1, 0,
|
||||
},
|
||||
3, 3, []Rotation{0, A90, A180, A270},
|
||||
[][]byte{
|
||||
{
|
||||
0, 1, 0,
|
||||
1, 1, 1,
|
||||
0, 1, 0,
|
||||
},
|
||||
{
|
||||
0, 1, 0,
|
||||
1, 1, 1,
|
||||
0, 1, 0,
|
||||
},
|
||||
{
|
||||
0, 1, 0,
|
||||
1, 1, 1,
|
||||
0, 1, 0,
|
||||
},
|
||||
{
|
||||
0, 1, 0,
|
||||
1, 1, 1,
|
||||
0, 1, 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
[]byte{
|
||||
1, 2,
|
||||
3, 4,
|
||||
5, 6,
|
||||
7, 8,
|
||||
},
|
||||
2, 4, []Rotation{0, A90, A180, A270},
|
||||
[][]byte{
|
||||
{
|
||||
1, 2,
|
||||
3, 4,
|
||||
5, 6,
|
||||
7, 8,
|
||||
},
|
||||
{
|
||||
2, 4, 6, 8,
|
||||
1, 3, 5, 7,
|
||||
},
|
||||
{
|
||||
8, 7,
|
||||
6, 5,
|
||||
4, 3,
|
||||
2, 1,
|
||||
},
|
||||
{
|
||||
7, 5, 3, 1,
|
||||
8, 6, 4, 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
// a square
|
||||
[]byte{
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 1, 1, 1, 1, 1, 1, 0,
|
||||
0, 1, 1, 1, 1, 1, 1, 0,
|
||||
0, 1, 0, 0, 0, 0, 1, 0,
|
||||
0, 1, 1, 1, 1, 1, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 1,
|
||||
},
|
||||
8, 6, []Rotation{0, A90, A180, A270},
|
||||
[][]byte{
|
||||
{
|
||||
// L // R
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 1, 1, 1, 1, 1, 1, 0,
|
||||
0, 1, 1, 1, 1, 1, 1, 0,
|
||||
0, 1, 0, 0, 0, 0, 1, 0,
|
||||
0, 1, 1, 1, 1, 1, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 1,
|
||||
},
|
||||
{
|
||||
0, 0, 0, 0, 0, 1,
|
||||
0, 1, 1, 1, 1, 0,
|
||||
0, 1, 1, 0, 1, 0,
|
||||
0, 1, 1, 0, 1, 0,
|
||||
0, 1, 1, 0, 1, 0,
|
||||
0, 1, 1, 0, 1, 0,
|
||||
0, 1, 1, 1, 1, 0,
|
||||
1, 0, 0, 0, 0, 0,
|
||||
},
|
||||
|
||||
{
|
||||
1, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 1, 1, 1, 1, 1, 1, 0,
|
||||
0, 1, 0, 0, 0, 0, 1, 0,
|
||||
0, 1, 1, 1, 1, 1, 1, 0,
|
||||
0, 1, 1, 1, 1, 1, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 1,
|
||||
},
|
||||
{
|
||||
0, 0, 0, 0, 0, 1,
|
||||
0, 1, 1, 1, 1, 0,
|
||||
0, 1, 0, 1, 1, 0,
|
||||
0, 1, 0, 1, 1, 0,
|
||||
0, 1, 0, 1, 1, 0,
|
||||
0, 1, 0, 1, 1, 0,
|
||||
0, 1, 1, 1, 1, 0,
|
||||
1, 0, 0, 0, 0, 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
for i, rot := range test.rotateHow {
|
||||
if output := exampleRotate(test.input, test.w, test.h, rot); !bytes.Equal(output, test.expected[i]) {
|
||||
t.Errorf(
|
||||
"Test fail for angle %v with %v that should be \n%v but it's \n%v",
|
||||
rot, test.input, test.expected[i], output)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBoundsAfterRotation(t *testing.T) {
|
||||
tests := []struct {
|
||||
dim []dimensions
|
||||
rotateHow []Rotation
|
||||
}{
|
||||
{
|
||||
// a combinatorics lib would be nice instead
|
||||
[]dimensions{
|
||||
// square
|
||||
{w: 100, h: 100},
|
||||
// even w/h
|
||||
{w: 100, h: 50},
|
||||
// even h/w
|
||||
{w: 50, h: 100},
|
||||
// odd even w/h
|
||||
{w: 77, h: 32},
|
||||
// even odd h/w
|
||||
{w: 32, h: 77},
|
||||
// just odd
|
||||
{w: 13, h: 19},
|
||||
},
|
||||
[]Rotation{0, A90, A180, A270},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
for _, rot := range test.rotateHow {
|
||||
for _, dim := range test.dim {
|
||||
|
||||
for y := 0; y < dim.h; y++ {
|
||||
for x := 0; x < dim.w; x++ {
|
||||
|
||||
xx, yy := rotate(int(rot), x, y, dim.w, dim.h)
|
||||
|
||||
if rot == A90 || rot == A270 { // is even
|
||||
yy, xx = xx, yy
|
||||
}
|
||||
|
||||
if xx < 0 || xx > dim.w {
|
||||
t.Errorf("Rot %v, coordinate x should be in range [0; %v]: %v", rot, dim.w-1, xx)
|
||||
}
|
||||
|
||||
if yy < 0 || yy > dim.h {
|
||||
t.Errorf("Rot %v, coordinate y should be in range [0; %v]: %v", rot, dim.h-1, yy)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// exampleRotate is an example of rotation usage.
|
||||
//
|
||||
// [1 2 3 4 5 6 7 8 9]
|
||||
// [7 4 1 8 5 2 9 6 3]
|
||||
func exampleRotate(data []uint8, w int, h int, rot Rotation) []uint8 {
|
||||
dest := make([]uint8, len(data))
|
||||
for y := 0; y < h; y++ {
|
||||
for x := 0; x < w; x++ {
|
||||
nx, ny := rotate(int(rot), x, y, w, h)
|
||||
stride := w
|
||||
if rot == A90 || rot == A270 { // is even
|
||||
stride = h
|
||||
}
|
||||
dest[nx+ny*stride] = data[x+y*w]
|
||||
}
|
||||
}
|
||||
return dest
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package manager
|
|||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/giongto35/cloud-game/v3/pkg/config"
|
||||
"github.com/giongto35/cloud-game/v3/pkg/logger"
|
||||
|
|
@ -31,6 +32,15 @@ func NewRemoteHttpManager(conf config.LibretroConfig, log *logger.Logger) Manage
|
|||
}
|
||||
log.Debug().Msgf("Using .lock file: %v", fileLock)
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(fileLock), 0770); err != nil {
|
||||
log.Error().Err(err).Msgf("couldn't create lock")
|
||||
} else {
|
||||
f, err := os.Create(fileLock)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msgf("couldn't create lock")
|
||||
}
|
||||
_ = f.Close()
|
||||
}
|
||||
ar, err := arch.Guess()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("couldn't get Libretro core file extension")
|
||||
|
|
@ -73,8 +83,16 @@ func CheckCores(conf config.Emulator, log *logger.Logger) error {
|
|||
|
||||
func (m *Manager) Sync() error {
|
||||
// IPC lock if multiple worker processes on the same machine
|
||||
m.fmu.Lock()
|
||||
defer m.fmu.Unlock()
|
||||
err := m.fmu.Lock()
|
||||
if err != nil {
|
||||
m.log.Error().Err(err).Msg("file lock fail")
|
||||
}
|
||||
defer func() {
|
||||
err := m.fmu.Unlock()
|
||||
if err != nil {
|
||||
m.log.Error().Err(err).Msg("file unlock fail")
|
||||
}
|
||||
}()
|
||||
|
||||
installed, err := m.GetInstalled(m.arch.LibExt)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import (
|
|||
"github.com/giongto35/cloud-game/v3/pkg/logger"
|
||||
"github.com/giongto35/cloud-game/v3/pkg/os"
|
||||
"github.com/giongto35/cloud-game/v3/pkg/worker/caged/libretro/graphics"
|
||||
"github.com/giongto35/cloud-game/v3/pkg/worker/caged/libretro/image"
|
||||
"github.com/giongto35/cloud-game/v3/pkg/worker/caged/libretro/repo/arch"
|
||||
"github.com/giongto35/cloud-game/v3/pkg/worker/thread"
|
||||
)
|
||||
|
|
@ -33,6 +32,12 @@ const KeyReleased = 0
|
|||
|
||||
const MaxPort int = 4
|
||||
|
||||
var (
|
||||
RGBA5551 = PixFmt{C: 0, BPP: 2} // BIT_FORMAT_SHORT_5_5_5_1 has 5 bits R, 5 bits G, 5 bits B, 1 bit alpha
|
||||
RGBA8888Rev = PixFmt{C: 1, BPP: 4} // BIT_FORMAT_INT_8_8_8_8_REV has 8 bits R, 8 bits G, 8 bits B, 8 bit alpha
|
||||
RGB565 = PixFmt{C: 2, BPP: 2} // BIT_FORMAT_SHORT_5_6_5 has 5 bits R, 6 bits G, 5 bits
|
||||
)
|
||||
|
||||
type Nanoarch struct {
|
||||
Handlers
|
||||
LastFrameTime int64
|
||||
|
|
@ -44,7 +49,7 @@ type Nanoarch struct {
|
|||
}
|
||||
options *map[string]string
|
||||
reserved chan struct{} // limits concurrent use
|
||||
Rot image.Rotation
|
||||
Rot uint
|
||||
serializeSize C.size_t
|
||||
stopped atomic.Bool
|
||||
sysAvInfo C.struct_retro_system_av_info
|
||||
|
|
@ -58,9 +63,8 @@ type Nanoarch struct {
|
|||
enabled bool
|
||||
autoCtx bool
|
||||
}
|
||||
BPP uint
|
||||
hw *C.struct_retro_hw_render_callback
|
||||
PixFmt uint32
|
||||
PixFmt PixFmt
|
||||
}
|
||||
vfr bool
|
||||
sdlCtx *graphics.SDL
|
||||
|
|
@ -78,7 +82,7 @@ type Handlers struct {
|
|||
type FrameInfo struct {
|
||||
W uint
|
||||
H uint
|
||||
Packed uint
|
||||
Stride uint
|
||||
}
|
||||
|
||||
type Metadata struct {
|
||||
|
|
@ -92,6 +96,24 @@ type Metadata struct {
|
|||
Hacks []string
|
||||
}
|
||||
|
||||
type PixFmt struct {
|
||||
C uint32
|
||||
BPP uint
|
||||
}
|
||||
|
||||
func (p PixFmt) String() string {
|
||||
switch p.C {
|
||||
case 0:
|
||||
return "RGBA5551/2"
|
||||
case 1:
|
||||
return "RGBA8888Rev/4"
|
||||
case 2:
|
||||
return "RGB565/2"
|
||||
default:
|
||||
return fmt.Sprintf("Unknown (%v/%v)", p.C, p.BPP)
|
||||
}
|
||||
}
|
||||
|
||||
// Nan0 is a global link for C callbacks to Go
|
||||
var Nan0 = Nanoarch{
|
||||
reserved: make(chan struct{}, 1), // this thing forbids concurrent use of the emulator
|
||||
|
|
@ -118,7 +140,7 @@ func NewNano(localPath string) *Nanoarch {
|
|||
|
||||
func (n *Nanoarch) AudioSampleRate() int { return int(n.sysAvInfo.timing.sample_rate) }
|
||||
func (n *Nanoarch) VideoFramerate() int { return int(n.sysAvInfo.timing.fps) }
|
||||
func (n *Nanoarch) IsPortrait() bool { return n.Rot == image.A90 || n.Rot == image.A270 }
|
||||
func (n *Nanoarch) IsPortrait() bool { return n.Rot == 90 || n.Rot == 270 }
|
||||
func (n *Nanoarch) GeometryBase() (int, int) {
|
||||
return int(n.sysAvInfo.geometry.base_width), int(n.sysAvInfo.geometry.base_height)
|
||||
}
|
||||
|
|
@ -252,7 +274,7 @@ func (n *Nanoarch) LoadGame(path string) error {
|
|||
|
||||
if n.Video.gl.enabled {
|
||||
//setRotation(image.F180) // flip Y coordinates of OpenGL
|
||||
bufS := uint(n.sysAvInfo.geometry.max_width*n.sysAvInfo.geometry.max_height) * n.Video.BPP
|
||||
bufS := uint(n.sysAvInfo.geometry.max_width*n.sysAvInfo.geometry.max_height) * n.Video.PixFmt.BPP
|
||||
graphics.SetBuffer(int(bufS))
|
||||
n.log.Info().Msgf("Set buffer: %v", byteCountBinary(int64(bufS)))
|
||||
if n.LibCo {
|
||||
|
|
@ -357,34 +379,33 @@ func (n *Nanoarch) IsStopped() bool { return n.stopped.Load() }
|
|||
func videoSetPixelFormat(format uint32) (C.bool, error) {
|
||||
switch format {
|
||||
case C.RETRO_PIXEL_FORMAT_0RGB1555:
|
||||
Nan0.Video.PixFmt = image.BitFormatShort5551
|
||||
Nan0.Video.PixFmt = RGBA5551
|
||||
if err := graphics.SetPixelFormat(graphics.UnsignedShort5551); err != nil {
|
||||
return false, fmt.Errorf("unknown pixel format %v", Nan0.Video.PixFmt)
|
||||
}
|
||||
Nan0.Video.BPP = 2
|
||||
// format is not implemented
|
||||
return false, fmt.Errorf("unsupported pixel type %v converter", format)
|
||||
case C.RETRO_PIXEL_FORMAT_XRGB8888:
|
||||
Nan0.Video.PixFmt = image.BitFormatInt8888Rev
|
||||
Nan0.Video.PixFmt = RGBA8888Rev
|
||||
if err := graphics.SetPixelFormat(graphics.UnsignedInt8888Rev); err != nil {
|
||||
return false, fmt.Errorf("unknown pixel format %v", Nan0.Video.PixFmt)
|
||||
}
|
||||
Nan0.Video.BPP = 4
|
||||
case C.RETRO_PIXEL_FORMAT_RGB565:
|
||||
Nan0.Video.PixFmt = image.BitFormatShort565
|
||||
Nan0.Video.PixFmt = RGB565
|
||||
if err := graphics.SetPixelFormat(graphics.UnsignedShort565); err != nil {
|
||||
return false, fmt.Errorf("unknown pixel format %v", Nan0.Video.PixFmt)
|
||||
}
|
||||
Nan0.Video.BPP = 2
|
||||
default:
|
||||
return false, fmt.Errorf("unknown pixel type %v", format)
|
||||
}
|
||||
Nan0.log.Info().Msgf("Pixel format: %v", Nan0.Video.PixFmt)
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func setRotation(rotation image.Rotation) {
|
||||
Nan0.Rot = rotation
|
||||
Nan0.log.Debug().Msgf("Image rotated %v°", map[uint]uint{0: 0, 1: 90, 2: 180, 3: 270}[uint(rotation)])
|
||||
func setRotation(rot uint) {
|
||||
Nan0.Rot = rot
|
||||
Nan0.log.Debug().Msgf("Image rotated %v°", rot)
|
||||
}
|
||||
|
||||
func printOpenGLDriverInfo() {
|
||||
|
|
@ -557,7 +578,7 @@ func coreVideoRefresh(data unsafe.Pointer, width, height uint, packed uint) {
|
|||
// calculate real frame width in pixels from packed data (realWidth >= width)
|
||||
// some cores or games output zero pitch, i.e. N64 Mupen
|
||||
if packed == 0 {
|
||||
packed = width * Nan0.Video.BPP
|
||||
packed = width * Nan0.Video.PixFmt.BPP
|
||||
}
|
||||
// calculate space for the video frame
|
||||
bytes := packed * height
|
||||
|
|
@ -575,7 +596,7 @@ func coreVideoRefresh(data unsafe.Pointer, width, height uint, packed uint) {
|
|||
// also we have an option of xN output frame magnification
|
||||
// so, it may be rescaled
|
||||
|
||||
Nan0.Handlers.OnVideo(data_, int32(dt), FrameInfo{W: width, H: height, Packed: packed})
|
||||
Nan0.Handlers.OnVideo(data_, int32(dt), FrameInfo{W: width, H: height, Stride: packed})
|
||||
}
|
||||
|
||||
//export coreInputPoll
|
||||
|
|
@ -665,8 +686,16 @@ func coreEnvironment(cmd C.unsigned, data unsafe.Pointer) C.bool {
|
|||
}
|
||||
|
||||
switch cmd {
|
||||
case C.RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO:
|
||||
av := *(*C.struct_retro_system_av_info)(data)
|
||||
Nan0.log.Info().Msgf(">>> SET SYS AV INFO: %v", av)
|
||||
return true
|
||||
case C.RETRO_ENVIRONMENT_SET_GEOMETRY:
|
||||
geom := *(*C.struct_retro_game_geometry)(data)
|
||||
Nan0.log.Info().Msgf(">>> GEOMETRY: %v", geom)
|
||||
return true
|
||||
case C.RETRO_ENVIRONMENT_SET_ROTATION:
|
||||
setRotation(image.Rotation(*(*uint)(data) % 4))
|
||||
setRotation((*(*uint)(data) % 4) * 90)
|
||||
return true
|
||||
case C.RETRO_ENVIRONMENT_GET_CAN_DUPE:
|
||||
*(*C.bool)(data) = C.bool(true)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package libretro
|
||||
|
||||
import (
|
||||
"image"
|
||||
"time"
|
||||
|
||||
"github.com/giongto35/cloud-game/v3/pkg/config"
|
||||
|
|
@ -15,23 +14,29 @@ type RecordingFrontend struct {
|
|||
rec *recorder.Recording
|
||||
}
|
||||
|
||||
// !to fix opaque image save
|
||||
|
||||
type opaque struct{ image.RGBA }
|
||||
|
||||
func (o *opaque) Opaque() bool { return true }
|
||||
|
||||
func WithRecording(fe Emulator, rec bool, user string, game string, conf config.Recording, log *logger.Logger) *RecordingFrontend {
|
||||
|
||||
pix := ""
|
||||
switch fe.PixFormat() {
|
||||
case 0:
|
||||
pix = "rgb1555"
|
||||
case 1:
|
||||
pix = "brga"
|
||||
case 2:
|
||||
pix = "rgb565"
|
||||
}
|
||||
|
||||
rr := &RecordingFrontend{Emulator: fe, rec: recorder.NewRecording(
|
||||
recorder.Meta{UserName: user},
|
||||
log,
|
||||
recorder.Options{
|
||||
Dir: conf.Folder,
|
||||
Game: game,
|
||||
ImageCompressionLevel: conf.CompressLevel,
|
||||
Name: conf.Name,
|
||||
Zip: conf.Zip,
|
||||
Vsync: true,
|
||||
Dir: conf.Folder,
|
||||
Game: game,
|
||||
Name: conf.Name,
|
||||
Zip: conf.Zip,
|
||||
Vsync: true,
|
||||
Flip: fe.Flipped(),
|
||||
Pix: pix,
|
||||
})}
|
||||
rr.ToggleRecording(rec, user)
|
||||
return rr
|
||||
|
|
@ -52,7 +57,7 @@ func (r *RecordingFrontend) SetAudioCb(fn func(app.Audio)) {
|
|||
func (r *RecordingFrontend) SetVideoCb(fn func(app.Video)) {
|
||||
r.Emulator.SetVideoCb(func(v app.Video) {
|
||||
if r.IsRecording() {
|
||||
r.rec.WriteVideo(recorder.Video{Image: &opaque{v.Frame}, Duration: time.Duration(v.Duration)})
|
||||
r.rec.WriteVideo(recorder.Video{Frame: recorder.Frame(v.Frame), Duration: time.Duration(v.Duration)})
|
||||
}
|
||||
fn(v)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ func (c *coordinator) HandleGameStart(rq api.StartGameRequest[com.Uid], w *Worke
|
|||
m.AudioSrcHz = app.AudioSampleRate()
|
||||
m.AudioFrame = w.conf.Encoder.Audio.Frame
|
||||
m.VideoW, m.VideoH = app.ViewportSize()
|
||||
m.VideoScale = app.Scale()
|
||||
|
||||
r.SetMedia(m)
|
||||
|
||||
|
|
|
|||
|
|
@ -103,11 +103,11 @@ func (s samples) stretch(size int) []int16 {
|
|||
}
|
||||
|
||||
type WebrtcMediaPipe struct {
|
||||
a *opus.Encoder
|
||||
v *encoder.Video
|
||||
onAudio func([]byte)
|
||||
opus *opus.Encoder
|
||||
audioBuf buffer
|
||||
log *logger.Logger
|
||||
enc *encoder.VideoEncoder
|
||||
|
||||
aConf config.Audio
|
||||
vConf config.Video
|
||||
|
|
@ -115,6 +115,7 @@ type WebrtcMediaPipe struct {
|
|||
AudioSrcHz int
|
||||
AudioFrame int
|
||||
VideoW, VideoH int
|
||||
VideoScale float64
|
||||
}
|
||||
|
||||
func NewWebRtcMediaPipe(ac config.Audio, vc config.Video, log *logger.Logger) *WebrtcMediaPipe {
|
||||
|
|
@ -126,8 +127,8 @@ func (wmp *WebrtcMediaPipe) SetAudioCb(cb func([]byte, int32)) {
|
|||
wmp.onAudio = func(bytes []byte) { cb(bytes, fr) }
|
||||
}
|
||||
func (wmp *WebrtcMediaPipe) Destroy() {
|
||||
if wmp.enc != nil {
|
||||
wmp.enc.Stop()
|
||||
if wmp.v != nil {
|
||||
wmp.v.Stop()
|
||||
}
|
||||
}
|
||||
func (wmp *WebrtcMediaPipe) PushAudio(audio []int16) { wmp.audioBuf.write(audio, wmp.encodeAudio) }
|
||||
|
|
@ -136,7 +137,7 @@ func (wmp *WebrtcMediaPipe) Init() error {
|
|||
if err := wmp.initAudio(wmp.AudioSrcHz, wmp.AudioFrame); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := wmp.initVideo(wmp.VideoW, wmp.VideoH, wmp.vConf); err != nil {
|
||||
if err := wmp.initVideo(wmp.VideoW, wmp.VideoH, wmp.VideoScale, wmp.vConf); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
|
@ -148,7 +149,7 @@ func (wmp *WebrtcMediaPipe) initAudio(srcHz int, frameSize int) error {
|
|||
return fmt.Errorf("opus fail: %w", err)
|
||||
}
|
||||
wmp.log.Debug().Msgf("Opus: %v", au.GetInfo())
|
||||
wmp.opus = au
|
||||
wmp.a = au
|
||||
buf := newBuffer(frame(srcHz, frameSize))
|
||||
dstHz, _ := au.SampleRate()
|
||||
if srcHz != dstHz {
|
||||
|
|
@ -160,7 +161,7 @@ func (wmp *WebrtcMediaPipe) initAudio(srcHz int, frameSize int) error {
|
|||
}
|
||||
|
||||
func (wmp *WebrtcMediaPipe) encodeAudio(pcm samples) {
|
||||
data, err := wmp.opus.Encode(pcm)
|
||||
data, err := wmp.a.Encode(pcm)
|
||||
audioPool.Put((*[]int16)(&pcm))
|
||||
if err != nil {
|
||||
wmp.log.Error().Err(err).Msgf("opus encode fail")
|
||||
|
|
@ -169,25 +170,36 @@ func (wmp *WebrtcMediaPipe) encodeAudio(pcm samples) {
|
|||
wmp.onAudio(data)
|
||||
}
|
||||
|
||||
func (wmp *WebrtcMediaPipe) initVideo(w, h int, conf config.Video) error {
|
||||
func (wmp *WebrtcMediaPipe) initVideo(w, h int, scale float64, conf config.Video) error {
|
||||
var enc encoder.Encoder
|
||||
var err error
|
||||
|
||||
sw, sh := round(w, scale), round(h, scale)
|
||||
|
||||
wmp.log.Debug().Msgf("Scale: %vx%v -> %vx%v", w, h, sw, sh)
|
||||
|
||||
wmp.log.Info().Msgf("Video codec: %v", conf.Codec)
|
||||
if conf.Codec == string(encoder.H264) {
|
||||
wmp.log.Debug().Msgf("x264: build v%v", h264.LibVersion())
|
||||
opts := h264.Options(conf.H264)
|
||||
enc, err = h264.NewEncoder(w, h, &opts)
|
||||
enc, err = h264.NewEncoder(sw, sh, &opts)
|
||||
} else {
|
||||
opts := vpx.Options(conf.Vpx)
|
||||
enc, err = vpx.NewEncoder(w, h, &opts)
|
||||
enc, err = vpx.NewEncoder(sw, sh, &opts)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't create a video encoder: %w", err)
|
||||
}
|
||||
wmp.enc = encoder.NewVideoEncoder(enc, w, h, conf.Concurrency, wmp.log)
|
||||
wmp.v = encoder.NewVideoEncoder(enc, w, h, scale, wmp.log)
|
||||
wmp.log.Debug().Msgf("%v", wmp.v.Info())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wmp *WebrtcMediaPipe) ProcessVideo(v app.Video) []byte { return wmp.enc.Encode(&v.Frame) }
|
||||
func round(x int, scale float64) int { return (int(float64(x)*scale) + 1) & ^1 }
|
||||
|
||||
func (wmp *WebrtcMediaPipe) SetVideoFlip(b bool) { wmp.enc.SetFlip(b) }
|
||||
func (wmp *WebrtcMediaPipe) ProcessVideo(v app.Video) []byte {
|
||||
return wmp.v.Encode(encoder.InFrame(v.Frame))
|
||||
}
|
||||
func (wmp *WebrtcMediaPipe) SetPixFmt(f uint32) { wmp.v.SetPixFormat(f) }
|
||||
func (wmp *WebrtcMediaPipe) SetVideoFlip(b bool) { wmp.v.SetFlip(b) }
|
||||
func (wmp *WebrtcMediaPipe) SetRot(r uint) { wmp.v.SetRot(r) }
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ func run(w, h int, cod encoder.VideoCodec, count int, a *image.RGBA, b *image.RG
|
|||
}
|
||||
|
||||
logger.SetGlobalLevel(logger.Disabled)
|
||||
ve := encoder.NewVideoEncoder(enc, w, h, 8, l)
|
||||
ve := encoder.NewVideoEncoder(enc, w, h, 1, l)
|
||||
defer ve.Stop()
|
||||
|
||||
if a == nil {
|
||||
|
|
@ -61,7 +61,12 @@ func run(w, h int, cod encoder.VideoCodec, count int, a *image.RGBA, b *image.RG
|
|||
if i%2 == 0 {
|
||||
im = b
|
||||
}
|
||||
out := ve.Encode(im)
|
||||
out := ve.Encode(encoder.InFrame{
|
||||
Data: im.Pix,
|
||||
Stride: im.Stride,
|
||||
W: im.Bounds().Dx(),
|
||||
H: im.Bounds().Dy(),
|
||||
})
|
||||
if out == nil {
|
||||
backend.Fatalf("encoder closed abnormally")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ const demuxFile = "input.txt"
|
|||
// ffmpeg concat demuxer, see: https://ffmpeg.org/ffmpeg-formats.html#concat
|
||||
// example:
|
||||
//
|
||||
// !to change
|
||||
//
|
||||
// ffmpeg -f concat -i input.txt \
|
||||
// -ac 2 -channel_layout stereo -i audio.wav \
|
||||
// -b:a 192K -crf 23 -vf fps=30 -pix_fmt yuv420p \
|
||||
|
|
@ -25,9 +27,17 @@ func createFfmpegMuxFile(dir string, fPattern string, frameTimes []time.Duration
|
|||
return err
|
||||
}
|
||||
defer func() { er = demux.Close() }()
|
||||
_, err = demux.WriteString(
|
||||
fmt.Sprintf("ffconcat version 1.0\n# v: 1\n# date: %v\n# game: %v\n# fps: %v\n# freq (hz): %v\n\n",
|
||||
time.Now().Format("20060102"), opts.Game, opts.Fps, opts.Frequency))
|
||||
|
||||
b := strings.Builder{}
|
||||
|
||||
b.WriteString("ffconcat version 1.0\n")
|
||||
b.WriteString(meta("v", "1"))
|
||||
b.WriteString(meta("date", time.Now().Format("20060102")))
|
||||
b.WriteString(meta("game", opts.Game))
|
||||
b.WriteString(meta("fps", opts.Fps))
|
||||
b.WriteString(meta("freq", opts.Frequency))
|
||||
b.WriteString(meta("pix", opts.Pix))
|
||||
_, err = demux.WriteString(fmt.Sprintf("%s\n", b.String()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -51,7 +61,9 @@ func createFfmpegMuxFile(dir string, fPattern string, frameTimes []time.Duration
|
|||
}
|
||||
i++
|
||||
}
|
||||
inf := fmt.Sprintf("file %v\nduration %f\n", name, dur)
|
||||
w, h, s := ExtractFileInfo(file.Name())
|
||||
inf := fmt.Sprintf("file %v\nduration %f\n%s%s%s", name, dur,
|
||||
metaf("width", w), metaf("height", h), metaf("stride", s))
|
||||
if _, err := demux.WriteString(inf); err != nil {
|
||||
er = err
|
||||
}
|
||||
|
|
@ -61,3 +73,11 @@ func createFfmpegMuxFile(dir string, fPattern string, frameTimes []time.Duration
|
|||
}
|
||||
return er
|
||||
}
|
||||
|
||||
// meta adds stream_meta key value line.
|
||||
func meta(key string, value any) string { return fmt.Sprintf("stream_meta %s '%v'\n", key, value) }
|
||||
|
||||
// metaf adds file_packet_meta key value line.
|
||||
func metaf(key string, value any) string {
|
||||
return fmt.Sprintf("file_packet_meta %s '%v'\n", key, value)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,18 @@
|
|||
package recorder
|
||||
|
||||
type Options struct {
|
||||
Dir string
|
||||
Fps float64
|
||||
Frequency int
|
||||
Game string
|
||||
ImageCompressionLevel int
|
||||
Name string
|
||||
Zip bool
|
||||
Vsync bool
|
||||
Dir string
|
||||
Fps float64
|
||||
W int
|
||||
H int
|
||||
Stride int
|
||||
Flip bool
|
||||
Frequency int
|
||||
Pix string
|
||||
Game string
|
||||
Name string
|
||||
Zip bool
|
||||
Vsync bool
|
||||
}
|
||||
|
||||
type Meta struct {
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
package recorder
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/png"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type pngStream struct {
|
||||
dir string
|
||||
e *png.Encoder
|
||||
id uint32
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
const videoFile = "f%07d.png"
|
||||
|
||||
type pool struct{ sync.Pool }
|
||||
|
||||
func pngBuf() *pool { return &pool{sync.Pool{New: func() any { return &png.EncoderBuffer{} }}} }
|
||||
func (p *pool) Get() *png.EncoderBuffer { return p.Pool.Get().(*png.EncoderBuffer) }
|
||||
func (p *pool) Put(b *png.EncoderBuffer) { p.Pool.Put(b) }
|
||||
|
||||
func newPngStream(dir string, opts Options) (*pngStream, error) {
|
||||
return &pngStream{
|
||||
dir: dir,
|
||||
e: &png.Encoder{
|
||||
CompressionLevel: png.CompressionLevel(opts.ImageCompressionLevel),
|
||||
BufferPool: pngBuf(),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *pngStream) Close() error {
|
||||
atomic.StoreUint32(&p.id, 0)
|
||||
p.wg.Wait()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *pngStream) Write(data Video) {
|
||||
fileName := fmt.Sprintf(videoFile, atomic.AddUint32(&p.id, 1))
|
||||
p.wg.Add(1)
|
||||
go p.saveImage(fileName, data.Image)
|
||||
}
|
||||
|
||||
func (p *pngStream) saveImage(fileName string, img image.Image) {
|
||||
var buf bytes.Buffer
|
||||
x, y := (img).Bounds().Dx(), (img).Bounds().Dy()
|
||||
buf.Grow(x * y * 4)
|
||||
|
||||
if err := p.e.Encode(&buf, img); err != nil {
|
||||
log.Printf("p err: %v", err)
|
||||
} else {
|
||||
file, err := os.Create(filepath.Join(p.dir, fileName))
|
||||
if err != nil {
|
||||
log.Printf("c err: %v", err)
|
||||
}
|
||||
if _, err = file.Write(buf.Bytes()); err != nil {
|
||||
log.Printf("f err: %v", err)
|
||||
}
|
||||
if err = file.Close(); err != nil {
|
||||
log.Printf("fc err: %v", err)
|
||||
}
|
||||
}
|
||||
p.wg.Done()
|
||||
}
|
||||
66
pkg/worker/recorder/rawstream.go
Normal file
66
pkg/worker/recorder/rawstream.go
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
package recorder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type rawStream struct {
|
||||
dir string
|
||||
id uint32
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
const videoFile = "f%07d__%dx%d__%d.raw"
|
||||
|
||||
func newRawStream(dir string) (*rawStream, error) {
|
||||
return &rawStream{dir: dir}, nil
|
||||
}
|
||||
|
||||
func (p *rawStream) Close() error {
|
||||
atomic.StoreUint32(&p.id, 0)
|
||||
p.wg.Wait()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *rawStream) Write(data Video) {
|
||||
i := atomic.AddUint32(&p.id, 1)
|
||||
fileName := fmt.Sprintf(videoFile, i, data.Frame.W, data.Frame.H, data.Frame.Stride)
|
||||
p.wg.Add(1)
|
||||
go p.saveFrame(fileName, data.Frame)
|
||||
}
|
||||
|
||||
func (p *rawStream) saveFrame(fileName string, frame Frame) {
|
||||
file, err := os.Create(filepath.Join(p.dir, fileName))
|
||||
if err != nil {
|
||||
log.Printf("c err: %v", err)
|
||||
}
|
||||
if _, err = file.Write(frame.Data); err != nil {
|
||||
log.Printf("f err: %v", err)
|
||||
}
|
||||
|
||||
if err = file.Close(); err != nil {
|
||||
log.Printf("fc err: %v", err)
|
||||
}
|
||||
p.wg.Done()
|
||||
}
|
||||
|
||||
func ExtractFileInfo(name string) (w, h, st string) {
|
||||
s1 := strings.Split(name, "__")
|
||||
if len(s1) > 1 {
|
||||
s12 := strings.Split(s1[1], "x")
|
||||
if len(s12) > 1 {
|
||||
w, h = s12[0], s12[1]
|
||||
}
|
||||
s21 := strings.TrimSuffix(s1[2], filepath.Ext(s1[2]))
|
||||
if s21 != "" {
|
||||
st = s21
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
package recorder
|
||||
|
||||
import (
|
||||
"image"
|
||||
"io"
|
||||
"math/rand"
|
||||
"os"
|
||||
|
|
@ -60,9 +59,14 @@ type (
|
|||
Duration time.Duration
|
||||
}
|
||||
Video struct {
|
||||
Image image.Image
|
||||
Frame Frame
|
||||
Duration time.Duration
|
||||
}
|
||||
Frame struct {
|
||||
Data []byte
|
||||
Stride int
|
||||
W, H int
|
||||
}
|
||||
)
|
||||
|
||||
// NewRecording creates new media recorder for the emulator.
|
||||
|
|
@ -96,7 +100,7 @@ func (r *Recording) Start() {
|
|||
r.log.Fatal().Err(err)
|
||||
}
|
||||
r.audio = audio
|
||||
video, err := newPngStream(path, r.opts)
|
||||
video, err := newRawStream(path)
|
||||
if err != nil {
|
||||
r.log.Fatal().Err(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,13 +30,12 @@ func TestName(t *testing.T) {
|
|||
Meta{UserName: "test"},
|
||||
logger.Default(),
|
||||
Options{
|
||||
Dir: dir,
|
||||
Fps: 60,
|
||||
Frequency: 10,
|
||||
Game: fmt.Sprintf("test_game_%v", rand.Int()),
|
||||
ImageCompressionLevel: 0,
|
||||
Name: "test",
|
||||
Zip: false,
|
||||
Dir: dir,
|
||||
Fps: 60,
|
||||
Frequency: 10,
|
||||
Game: fmt.Sprintf("test_game_%v", rand.Int()),
|
||||
Name: "test",
|
||||
Zip: false,
|
||||
})
|
||||
recorder.Set(true, "test_user")
|
||||
|
||||
|
|
@ -45,11 +44,11 @@ func TestName(t *testing.T) {
|
|||
var imgWg, audioWg sync.WaitGroup
|
||||
imgWg.Add(iterations)
|
||||
audioWg.Add(iterations)
|
||||
img := generateImage(100, 100)
|
||||
frame := genFrame(100, 100)
|
||||
|
||||
for i := 0; i < 222; i++ {
|
||||
go func() {
|
||||
recorder.WriteVideo(Video{Image: img, Duration: 16 * time.Millisecond})
|
||||
recorder.WriteVideo(Video{Frame: frame, Duration: 16 * time.Millisecond})
|
||||
imgWg.Done()
|
||||
}()
|
||||
go func() {
|
||||
|
|
@ -66,17 +65,14 @@ func TestName(t *testing.T) {
|
|||
}
|
||||
|
||||
func BenchmarkNewRecording100x100(b *testing.B) {
|
||||
benchmarkRecorder(100, 100, 0, b)
|
||||
benchmarkRecorder(100, 100, b)
|
||||
}
|
||||
|
||||
func BenchmarkNewRecording320x240_compressed(b *testing.B) {
|
||||
benchmarkRecorder(320, 240, 0, b)
|
||||
}
|
||||
func BenchmarkNewRecording320x240_nocompress(b *testing.B) {
|
||||
benchmarkRecorder(320, 240, -1, b)
|
||||
func BenchmarkNewRecording320x240(b *testing.B) {
|
||||
benchmarkRecorder(320, 240, b)
|
||||
}
|
||||
|
||||
func benchmarkRecorder(w, h int, comp int, b *testing.B) {
|
||||
func benchmarkRecorder(w, h int, b *testing.B) {
|
||||
b.StopTimer()
|
||||
|
||||
dir, err := os.MkdirTemp("", "rec_bench_")
|
||||
|
|
@ -89,8 +85,8 @@ func benchmarkRecorder(w, h int, comp int, b *testing.B) {
|
|||
}
|
||||
}()
|
||||
|
||||
image1 := generateImage(w, h)
|
||||
image2 := generateImage(w, h)
|
||||
frame1 := genFrame(w, h)
|
||||
frame2 := genFrame(w, h)
|
||||
|
||||
var bytes int64 = 0
|
||||
|
||||
|
|
@ -103,25 +99,24 @@ func benchmarkRecorder(w, h int, comp int, b *testing.B) {
|
|||
Meta{UserName: "test"},
|
||||
logger.Default(),
|
||||
Options{
|
||||
Dir: dir,
|
||||
Fps: 60,
|
||||
Frequency: 10,
|
||||
Game: fmt.Sprintf("test_game_%v", rand.Int()),
|
||||
ImageCompressionLevel: comp,
|
||||
Name: "",
|
||||
Zip: false,
|
||||
Dir: dir,
|
||||
Fps: 60,
|
||||
Frequency: 10,
|
||||
Game: fmt.Sprintf("test_game_%v", rand.Int()),
|
||||
Name: "",
|
||||
Zip: false,
|
||||
})
|
||||
recorder.Set(true, "test_user")
|
||||
samples := []int16{0, 0, 0, 0, 0, 1, 11, 11, 11, 1}
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
im := image1
|
||||
f := frame1
|
||||
if i%2 == 0 {
|
||||
im = image2
|
||||
f = frame2
|
||||
}
|
||||
go func() {
|
||||
recorder.WriteVideo(Video{Image: im, Duration: 16 * time.Millisecond})
|
||||
atomic.AddInt64(&bytes, int64(len(im.(*image.RGBA).Pix)))
|
||||
recorder.WriteVideo(Video{Frame: f, Duration: 16 * time.Millisecond})
|
||||
atomic.AddInt64(&bytes, int64(len(f.Data)))
|
||||
ticks.Done()
|
||||
}()
|
||||
go func() {
|
||||
|
|
@ -137,14 +132,19 @@ func benchmarkRecorder(w, h int, comp int, b *testing.B) {
|
|||
}
|
||||
}
|
||||
|
||||
func generateImage(w, h int) image.Image {
|
||||
func genFrame(w, h int) Frame {
|
||||
img := image.NewRGBA(image.Rect(0, 0, w, h))
|
||||
for x := 0; x < w; x++ {
|
||||
for y := 0; y < h; y++ {
|
||||
img.Set(x, y, randomColor())
|
||||
}
|
||||
}
|
||||
return img
|
||||
return Frame{
|
||||
Data: img.Pix,
|
||||
Stride: img.Stride,
|
||||
W: img.Bounds().Dx(),
|
||||
H: img.Bounds().Dy(),
|
||||
}
|
||||
}
|
||||
|
||||
var rnd = rand.New(rand.NewSource(time.Now().Unix()))
|
||||
|
|
|
|||
|
|
@ -19,16 +19,20 @@ import (
|
|||
"github.com/giongto35/cloud-game/v3/pkg/com"
|
||||
"github.com/giongto35/cloud-game/v3/pkg/config"
|
||||
"github.com/giongto35/cloud-game/v3/pkg/encoder"
|
||||
"github.com/giongto35/cloud-game/v3/pkg/encoder/color/bgra"
|
||||
"github.com/giongto35/cloud-game/v3/pkg/encoder/color/rgb565"
|
||||
"github.com/giongto35/cloud-game/v3/pkg/encoder/color/rgba"
|
||||
"github.com/giongto35/cloud-game/v3/pkg/games"
|
||||
"github.com/giongto35/cloud-game/v3/pkg/logger"
|
||||
"github.com/giongto35/cloud-game/v3/pkg/worker/caged"
|
||||
"github.com/giongto35/cloud-game/v3/pkg/worker/caged/app"
|
||||
canvas "github.com/giongto35/cloud-game/v3/pkg/worker/caged/libretro/image"
|
||||
"github.com/giongto35/cloud-game/v3/pkg/worker/media"
|
||||
"github.com/giongto35/cloud-game/v3/pkg/worker/thread"
|
||||
"golang.org/x/image/font"
|
||||
"golang.org/x/image/font/basicfont"
|
||||
"golang.org/x/image/math/fixed"
|
||||
|
||||
_ "github.com/giongto35/cloud-game/v3/test"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -58,13 +62,15 @@ func (r testRoom) Close() {
|
|||
time.Sleep(2 * time.Second) // hack: wait room destruction (atm impossible to tell)
|
||||
}
|
||||
|
||||
func (r testRoom) WaitFrames(n int) canvas.Frame {
|
||||
var frame canvas.Frame
|
||||
func (r testRoom) WaitFrame(n int) app.RawFrame {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(n)
|
||||
wg.Add(1)
|
||||
target := app.RawFrame{}
|
||||
WithEmulator(r.app).SetVideoCb(func(v app.Video) {
|
||||
if n > 0 {
|
||||
frame = (&canvas.Frame{RGBA: v.Frame}).Copy()
|
||||
if n == 1 {
|
||||
target = v.Frame
|
||||
target.Data = make([]byte, len(v.Frame.Data))
|
||||
copy(target.Data, v.Frame.Data)
|
||||
wg.Done()
|
||||
}
|
||||
n--
|
||||
|
|
@ -73,7 +79,7 @@ func (r testRoom) WaitFrames(n int) canvas.Frame {
|
|||
r.StartApp()
|
||||
}
|
||||
wg.Wait()
|
||||
return frame
|
||||
return target
|
||||
}
|
||||
|
||||
type testParams struct {
|
||||
|
|
@ -81,11 +87,11 @@ type testParams struct {
|
|||
game games.GameMetadata
|
||||
codecs []codec
|
||||
frames int
|
||||
color int
|
||||
}
|
||||
|
||||
// Store absolute path to test games
|
||||
var testTempDir = filepath.Join(os.TempDir(), "cloud-game-core-tests")
|
||||
var root = ""
|
||||
|
||||
// games
|
||||
var (
|
||||
|
|
@ -94,12 +100,6 @@ var (
|
|||
fd = games.GameMetadata{Name: "Florian Demo", Type: "n64", Path: "Sample Demo by Florian (PD).z64", System: "n64"}
|
||||
)
|
||||
|
||||
func init() {
|
||||
runtime.LockOSThread()
|
||||
p, _ := filepath.Abs("../../../")
|
||||
root = p + string(filepath.Separator)
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
flag.BoolVar(&renderFrames, "renderFrames", false, "Render frames for eye testing purposes")
|
||||
flag.StringVar(&outputPath, "outputPath", "./", "Output path for generated files")
|
||||
|
|
@ -115,40 +115,51 @@ func TestRoom(t *testing.T) {
|
|||
|
||||
for _, test := range tests {
|
||||
room := room(conf{codec: test.codecs[0], game: test.game})
|
||||
room.WaitFrames(test.frames)
|
||||
room.WaitFrame(test.frames)
|
||||
room.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func TestAll(t *testing.T) {
|
||||
tests := []testParams{
|
||||
{game: sushi, frames: 150},
|
||||
{game: alwas, frames: 50},
|
||||
{game: fd, frames: 50, system: "main-thread"},
|
||||
{game: sushi, frames: 150, color: 2},
|
||||
{game: alwas, frames: 50, color: 1},
|
||||
{game: fd, frames: 50, system: "gl", color: 1},
|
||||
}
|
||||
|
||||
crc32q := crc32.MakeTable(0xD5828281)
|
||||
|
||||
for _, test := range tests {
|
||||
var frame app.RawFrame
|
||||
room := room(conf{game: test.game, codec: encoder.VP8, autoGlContext: autoGlContext, autoAppStart: false})
|
||||
var frame canvas.Frame
|
||||
if test.system == "main-thread" {
|
||||
thread.Main(func() {
|
||||
frame = room.WaitFrames(test.frames)
|
||||
room.Close()
|
||||
})
|
||||
} else {
|
||||
frame = room.WaitFrames(test.frames)
|
||||
room.Close()
|
||||
}
|
||||
flip := test.system == "gl"
|
||||
thread.Main(func() { frame = room.WaitFrame(test.frames) })
|
||||
room.Close()
|
||||
|
||||
if renderFrames {
|
||||
tag := fmt.Sprintf("%v-%v-0x%08x", runtime.GOOS, test.game.Type, crc32.Checksum(frame.Pix, crc32q))
|
||||
dumpCanvas(&frame, tag, fmt.Sprintf("%v [%v]", tag, test.frames), outputPath)
|
||||
rect := image.Rect(0, 0, frame.W, frame.H)
|
||||
var src image.Image
|
||||
if test.color == 1 {
|
||||
src1 := bgra.NewBGRA(rect)
|
||||
src1.Pix = frame.Data
|
||||
src1.Stride = frame.Stride
|
||||
src = src1
|
||||
} else {
|
||||
if test.color == 2 {
|
||||
src1 := rgb565.NewRGB565(rect)
|
||||
src1.Pix = frame.Data
|
||||
src1.Stride = frame.Stride
|
||||
src = src1
|
||||
}
|
||||
}
|
||||
dst := rgba.ToRGBA(src, flip)
|
||||
tag := fmt.Sprintf("%v-%v-0x%08x", runtime.GOOS, test.game.Type, crc32.Checksum(frame.Data, crc32q))
|
||||
dumpCanvas(dst, tag, fmt.Sprintf("%v [%v]", tag, test.frames), outputPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func dumpCanvas(frame *canvas.Frame, name string, caption string, path string) {
|
||||
func dumpCanvas(frame *image.RGBA, name string, caption string, path string) {
|
||||
// slap 'em caption
|
||||
if caption != "" {
|
||||
draw.Draw(frame, image.Rect(8, 8, 8+len(caption)*7+3, 24), &image.Uniform{C: color.RGBA{}}, image.Point{}, draw.Src)
|
||||
|
|
@ -187,16 +198,17 @@ func room(cfg conf) testRoom {
|
|||
panic(err)
|
||||
}
|
||||
|
||||
conf.Worker.Library.BasePath = filepath.FromSlash(root + "/assets/games")
|
||||
conf.Emulator.Libretro.Cores.Repo.ExtLock = expand("tests", ".cr", "cloud-game.lock")
|
||||
conf.Emulator.LocalPath = expand("tests", conf.Emulator.LocalPath)
|
||||
conf.Emulator.Storage = expand("tests", "storage")
|
||||
|
||||
conf.Encoder.Video.Codec = string(cfg.codec)
|
||||
|
||||
fixEmulators(&conf, cfg.autoGlContext)
|
||||
l := logger.NewConsole(conf.Worker.Debug, "w", false)
|
||||
if cfg.noLog {
|
||||
logger.SetGlobalLevel(logger.Disabled)
|
||||
}
|
||||
|
||||
conf.Encoder.Video.Codec = string(cfg.codec)
|
||||
|
||||
id := cfg.roomName
|
||||
if id == "" {
|
||||
id = games.GenerateRoomID(cfg.game.Name)
|
||||
|
|
@ -218,6 +230,7 @@ func room(cfg conf) testRoom {
|
|||
m.AudioSrcHz = emu.AudioSampleRate()
|
||||
m.AudioFrame = conf.Encoder.Audio.Frame
|
||||
m.VideoW, m.VideoH = emu.ViewportSize()
|
||||
m.VideoScale = emu.Scale()
|
||||
if err := m.Init(); err != nil {
|
||||
l.Fatal().Err(err).Msgf("no init")
|
||||
}
|
||||
|
|
@ -230,22 +243,6 @@ func room(cfg conf) testRoom {
|
|||
return testRoom{Room: room, started: cfg.autoAppStart}
|
||||
}
|
||||
|
||||
// fixEmulators makes absolute game paths in global GameList and passes GL context config.
|
||||
// hack: emulator paths should be absolute and visible to the tests.
|
||||
func fixEmulators(config *config.WorkerConfig, autoGlContext bool) {
|
||||
config.Emulator.Libretro.Cores.Paths.Libs =
|
||||
filepath.FromSlash(root + config.Emulator.Libretro.Cores.Paths.Libs)
|
||||
config.Emulator.LocalPath = filepath.FromSlash(filepath.Join(root, "tests", config.Emulator.LocalPath))
|
||||
config.Emulator.Storage = filepath.FromSlash(filepath.Join(root, "tests", "storage"))
|
||||
|
||||
for k, conf := range config.Emulator.Libretro.Cores.List {
|
||||
if conf.IsGlAllowed && autoGlContext {
|
||||
conf.AutoGlContext = true
|
||||
}
|
||||
config.Emulator.Libretro.Cores.List[k] = conf
|
||||
}
|
||||
}
|
||||
|
||||
// Measures emulation performance of various
|
||||
// emulators and encoding options.
|
||||
func BenchmarkRoom(b *testing.B) {
|
||||
|
|
@ -263,7 +260,7 @@ func BenchmarkRoom(b *testing.B) {
|
|||
b.StopTimer()
|
||||
room := room(conf{game: bench.game, codec: cod, noLog: true})
|
||||
b.StartTimer()
|
||||
room.WaitFrames(bench.frames)
|
||||
room.WaitFrame(bench.frames)
|
||||
b.StopTimer()
|
||||
room.Room.Close()
|
||||
}
|
||||
|
|
@ -299,3 +296,9 @@ func TestRouter(t *testing.T) {
|
|||
router.SetRoom(nil)
|
||||
router.Close()
|
||||
}
|
||||
|
||||
// expand joins a list of file path elements.
|
||||
func expand(p ...string) string {
|
||||
ph, _ := filepath.Abs(filepath.FromSlash(filepath.Join(p...)))
|
||||
return ph
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,14 @@
|
|||
package thread
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func init() {
|
||||
runtime.LockOSThread()
|
||||
func TestMain(m *testing.M) {
|
||||
Wrap(func() { os.Exit(m.Run()) })
|
||||
}
|
||||
|
||||
func TestMainThread(t *testing.T) {
|
||||
value := 0
|
||||
fn := func() { value = 1 }
|
||||
Main(fn)
|
||||
if value != 1 {
|
||||
t.Errorf("wrong value %v", value)
|
||||
}
|
||||
_ = 10
|
||||
}
|
||||
|
|
|
|||
17
test/test.go
Normal file
17
test/test.go
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// runs tests from the root dir when imported
|
||||
|
||||
func init() {
|
||||
_, filename, _, _ := runtime.Caller(0)
|
||||
dir := path.Join(path.Dir(filename), "..")
|
||||
if err := os.Chdir(dir); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
0
test/testdata/raw/000_name_fourcc_width_height_stride
vendored
Normal file
0
test/testdata/raw/000_name_fourcc_width_height_stride
vendored
Normal file
BIN
test/testdata/raw/001_alsa_ABGR_256_240_1024.raw.zip
vendored
Normal file
BIN
test/testdata/raw/001_alsa_ABGR_256_240_1024.raw.zip
vendored
Normal file
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue