Skip to content

Commit

Permalink
#3457 forgot to add decoder file
Browse files Browse the repository at this point in the history
  • Loading branch information
totaam committed Feb 14, 2022
1 parent 5be3c6e commit 3edef02
Showing 1 changed file with 138 additions and 0 deletions.
138 changes: 138 additions & 0 deletions xpra/codecs/avif/decoder.pyx
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# This file is part of Xpra.
# Copyright (C) 2022 Antoine Martin <[email protected]>
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

from xpra.codecs.image_wrapper import ImageWrapper
from xpra.codecs.codec_debug import may_save_image

from libc.string cimport memset #pylint: disable=syntax-error
from xpra.buffers.membuf cimport getbuf, MemBuf #pylint: disable=syntax-error
from xpra.codecs.avif.avif cimport (
avifDecoder, avifResult, avifRGBImage, avifImage,
avifResultToString,
avifDecoderCreate, avifDecoderSetIOMemory,
avifDecoderParse, avifDecoderNextImage, avifDecoderDestroy,
avifDecoderNextImage, avifRGBImageSetDefaults, avifImageYUVToRGB,
AVIF_RESULT, AVIF_RESULT_OK, AVIF_RGB_FORMAT_BGRA,
)
from xpra.buffers.membuf cimport memalign, buffer_context

from xpra.log import Logger
log = Logger("encoder", "avif")

from libc.stdint cimport uint8_t, uint32_t, uintptr_t
from libc.stdlib cimport free

cdef extern from *:
ctypedef unsigned long size_t

cdef extern from "Python.h":
object PyMemoryView_FromMemory(char *mem, Py_ssize_t size, int flags)

cdef extern from "avif/avif.h":
int AVIF_VERSION_MAJOR
int AVIF_VERSION_MINOR
int AVIF_VERSION_PATCH


def get_version():
return (AVIF_VERSION_MAJOR, AVIF_VERSION_MINOR, AVIF_VERSION_PATCH)

def get_info():
return {
"version" : get_version(),
"encodings" : get_encodings(),
}

def get_encodings():
return ("avif", )


cdef check(avifResult r, message):
if r != AVIF_RESULT_OK:
err = avifResultToString(r).decode("latin1") or AVIF_RESULT.get(r, r)
raise Exception("%s : %s" % (message, err))

def decompress(data, options=None):
cdef avifRGBImage rgb
memset(&rgb, 0, sizeof(avifRGBImage))
cdef avifDecoder * decoder = avifDecoderCreate()
if decoder==NULL:
raise Exception("failed to create avif decoder")
cdef avifResult r
cdef size_t data_len
cdef const uint8_t* data_buf
cdef uint32_t width, height, stride
cdef uint8_t bpp = 32
cdef MemBuf pixels
cdef avifImage *image = NULL
cdef uint8_t has_alpha = (options or {}).get("alpha", 0)
try:
# Override decoder defaults here (codecChoice, requestedSource, ignoreExif, ignoreXMP, etc)
with buffer_context(data) as bc:
data_len = len(bc)
data_buf = <const uint8_t*> (<uintptr_t> int(bc))
r = avifDecoderSetIOMemory(decoder, data_buf, data_len)
check(r, "Cannot set IO on avifDecoder")

r = avifDecoderParse(decoder)
check(r, "Failed to decode image")

image = decoder.image
# Now available:
# * All decoder->image information other than pixel data:
# * width, height, depth
# * transformations (pasp, clap, irot, imir)
# * color profile (icc, CICP)
# * metadata (Exif, XMP)
# * decoder->alphaPresent
# * number of total images in the AVIF (decoder->imageCount)
# * overall image sequence timing (including per-frame timing with avifDecoderNthImageTiming())
width = image.width
height = image.height
log("avif parsed: %ux%u (%ubpc)\n", width, height, image.depth)
r = avifDecoderNextImage(decoder)
check(r, "failed to get next image")
# Now available (for this frame):
# * All decoder->image YUV pixel data (yuvFormat, yuvPlanes, yuvRange, yuvChromaSamplePosition, yuvRowBytes)
# * decoder->image alpha data (alphaRange, alphaPlane, alphaRowBytes)
# * this frame's sequence timing
avifRGBImageSetDefaults(&rgb, image)
# Override YUV(A)->RGB(A) defaults here: depth, format, chromaUpsampling, ignoreAlpha, alphaPremultiplied, libYUVUsage, etc
# Alternative: set rgb.pixels and rgb.rowBytes yourself, which should match your chosen rgb.format
# Be sure to use uint16_t* instead of uint8_t* for rgb.pixels/rgb.rowBytes if (rgb.depth > 8)
rgb_format = "BGRA"
if not has_alpha:
rgb.ignoreAlpha = 1
rgb_format = "BGRX"
bpp = 24
rgb.format = AVIF_RGB_FORMAT_BGRA
stride = width*4
pixels = getbuf(width*4*height)
rgb.pixels = <uint8_t *> pixels.get_mem()
rgb.rowBytes = stride
r = avifImageYUVToRGB(image, &rgb)
check(r, "Conversion from YUV failed")

# Now available:
# * RGB(A) pixel data (rgb.pixels, rgb.rowBytes)
if rgb.depth>8:
raise Exception("cannot handle depth %s" % rgb.depth)
may_save_image("avif", data)
return ImageWrapper(0, 0, width, height, memoryview(pixels), rgb_format, bpp, stride, ImageWrapper.PACKED)
finally:
avifDecoderDestroy(decoder)



def selftest(full=False):
w, h = 24, 16 #hard coded size of test data
for has_alpha, hexdata in ((True, "00000020667479706176696600000000617669666d6966316d6961664d413141000001a16d657461000000000000002868646c720000000000000000706963740000000000000000000000006c696261766966000000000e7069746d0000000000010000002c696c6f630000000044000002000100000001000001de00000018000200000001000001c9000000150000004269696e660000000000020000001a696e6665020000000001000061763031436f6c6f72000000001a696e6665020000000002000061763031416c706861000000001a69726566000000000000000e6175786c000200010001000000d769707270000000b16970636f0000001469737065000000000000001800000010000000107069786900000000030808080000000c617631438120000000000013636f6c726e636c780002000200028000000014697370650000000000000018000000100000000e706978690000000001080000000c6176314381001c0000000038617578430000000075726e3a6d7065673a6d706567423a636963703a73797374656d733a617578696c696172793a616c706861000000001e69706d6100000000000000020001040102830400020405068708000000356d64617412000a051810efed2a320a1000c5c0e0651476f01c12000a053810efed12320d100000c79949a86935c2b90c40"),
(False, "00000020667479706176696600000000617669666d6966316d6961664d413141000000f26d657461000000000000002868646c720000000000000000706963740000000000000000000000006c696261766966000000000e7069746d0000000000010000001e696c6f6300000000440000010001000000010000011a000000180000002869696e660000000000010000001a696e6665020000000001000061763031436f6c6f72000000006a697072700000004b6970636f0000001469737065000000000000001800000010000000107069786900000000030808080000000c617631438120000000000013636f6c726e636c78000200020002800000001769706d61000000000000000100010401028304000000206d64617412000a053810efed12320d100000c5c030e847ff81dca0c0")):
import binascii
bdata = binascii.unhexlify(hexdata)
img = decompress(bdata, {"alpha" : has_alpha})
assert img.get_width()==w and img.get_height()==h
assert len(img.get_pixels())>0
#print("compressed data(%s)=%s" % (has_alpha, binascii.hexlify(r)))

0 comments on commit 3edef02

Please sign in to comment.