Skip to content

Commit

Permalink
#487: implement a webp decoder using the advanced API so we can get p…
Browse files Browse the repository at this point in the history
…remultiplied alpha

git-svn-id: https://xpra.org/svn/Xpra/trunk@6509 3bb7dfac-3a0b-4e04-842a-767bc560f471
  • Loading branch information
totaam committed May 18, 2014
1 parent 9dd8912 commit 7630cd0
Show file tree
Hide file tree
Showing 5 changed files with 244 additions and 3 deletions.
4 changes: 4 additions & 0 deletions src/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -724,6 +724,7 @@ def pkgconfig(*pkgs_options, **ekw):
"xpra/codecs/enc_x264/encoder.c",
"xpra/codecs/enc_x265/encoder.c",
"xpra/codecs/webp/encode.c",
"xpra/codecs/webp/decode.c",
"xpra/codecs/dec_avcodec/decoder.c",
"xpra/codecs/dec_avcodec/constants.pxi",
"xpra/codecs/dec_avcodec2/decoder.c",
Expand Down Expand Up @@ -1550,6 +1551,9 @@ def cython_add(*args, **kwargs):
cython_add(Extension("xpra.codecs.webp.encode",
["xpra/codecs/webp/encode.pyx", buffers_c],
**webp_pkgconfig))
cython_add(Extension("xpra.codecs.webp.decode",
["xpra/codecs/webp/decode.pyx"]+membuffers_c,
**webp_pkgconfig))

toggle_packages(dec_avcodec_ENABLED, "xpra.codecs.dec_avcodec")
if dec_avcodec_ENABLED:
Expand Down
2 changes: 1 addition & 1 deletion src/xpra/client/ui_client_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ def do_get_core_encodings(self):
core_encodings = ["rgb24"]
#PIL:
core_encodings += get_PIL_decodings(get_codec("PIL"))
if has_codec("dec_webm") and "webp" not in core_encodings:
if (has_codec("dec_webm") or has_codec("dec_webp")) and "webp" not in core_encodings:
core_encodings.append("webp")
#we enable all the video decoders we know about,
#what will actually get used by the server will still depend on the csc modes supported
Expand Down
20 changes: 20 additions & 0 deletions src/xpra/client/window_backing_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,26 @@ def paint_image(self, coding, img_data, x, y, width, height, options, callbacks)
return False

def paint_webp(self, img_data, x, y, width, height, options, callbacks):
dec_webp = get_codec("dec_webp")
if dec_webp:
return self.paint_webp_using_cwebp(img_data, x, y, width, height, options, callbacks)
return self.paint_webp_using_webm(img_data, x, y, width, height, options, callbacks)

def paint_webp_using_cwebp(self, img_data, x, y, width, height, options, callbacks):
dec_webp = get_codec("dec_webp")
has_alpha = options.get("has_alpha", False)
buffer_wrapper, width, height, stride, has_alpha, rgb_format = dec_webp.decompress(img_data, has_alpha)
options["rgb_format"] = rgb_format
def free_buffer(*args):
buffer_wrapper.free()
callbacks.append(free_buffer)
data = buffer_wrapper.get_pixels()
if has_alpha:
return self.paint_rgb32(data, x, y, width, height, stride, options, callbacks)
else:
return self.paint_rgb24(data, x, y, width, height, stride, options, callbacks)

def paint_webp_using_webm(self, img_data, x, y, width, height, options, callbacks):
""" can be called from any thread """
dec_webm = get_codec("dec_webm")
assert dec_webm is not None, "webp decoder not found"
Expand Down
8 changes: 6 additions & 2 deletions src/xpra/codecs/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,10 @@ def load_codecs():

#webp via cython:
codec_import_check("enc_webp", "webp encoder", "xpra.codecs.webp", "xpra.codecs.webp.encode", "compress")
add_codec_version("webp", "xpra.codecs.webp.encode")
add_codec_version("enc_webp", "xpra.codecs.webp.encode")

codec_import_check("dec_webp", "webp decoder", "xpra.codecs.webp", "xpra.codecs.webp.decode", "decompress")
add_codec_version("dec_webp", "xpra.codecs.webp.decode")

#no bytearray (python 2.6 or later) or no bitmap handlers, no webm:
from xpra.os_util import builtins
Expand Down Expand Up @@ -164,7 +167,8 @@ def has_codec(name):
"dec_avcodec", "dec_avcodec2", \
"enc_webm", \
"dec_webm", \
"enc_webp"
"enc_webp", \
"dec_webp"

#note: this is just for defining the order of encodings,
#so we have both core encodings (rgb24/rgb32) and regular encodings (rgb) in here:
Expand Down
213 changes: 213 additions & 0 deletions src/xpra/codecs/webp/decode.pyx
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
# This file is part of Xpra.
# Copyright (C) 2014 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.

import time
import os

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


from libc.stdint cimport uint8_t, uint32_t

cdef extern from *:
ctypedef unsigned long size_t

cdef extern from "stdlib.h":
void free(void *ptr)

cdef extern from "Python.h":
ctypedef int Py_ssize_t

cdef extern from "../buffers/memalign.h":
void *xmemalign(size_t size)

cdef extern from "../buffers/buffers.h":
object memory_as_pybuffer(void* ptr, Py_ssize_t buf_len, int readonly)

cdef extern from "webp/decode.h":

int WebPGetDecoderVersion()

ctypedef int VP8StatusCode
VP8StatusCode VP8_STATUS_OK
VP8StatusCode VP8_STATUS_OUT_OF_MEMORY
VP8StatusCode VP8_STATUS_INVALID_PARAM
VP8StatusCode VP8_STATUS_BITSTREAM_ERROR
VP8StatusCode VP8_STATUS_UNSUPPORTED_FEATURE
VP8StatusCode VP8_STATUS_SUSPENDED
VP8StatusCode VP8_STATUS_USER_ABORT
VP8StatusCode VP8_STATUS_NOT_ENOUGH_DATA

ctypedef int WEBP_CSP_MODE
WEBP_CSP_MODE MODE_RGB
WEBP_CSP_MODE MODE_RGBA
WEBP_CSP_MODE MODE_BGR
WEBP_CSP_MODE MODE_BGRA
WEBP_CSP_MODE MODE_ARGB
WEBP_CSP_MODE MODE_RGBA_4444
WEBP_CSP_MODE MODE_RGB_565
#RGB-premultiplied transparent modes (alpha value is preserved)
WEBP_CSP_MODE MODE_rgbA
WEBP_CSP_MODE MODE_bgrA
WEBP_CSP_MODE MODE_Argb
WEBP_CSP_MODE MODE_rgbA_4444
#YUV modes must come after RGB ones.
WEBP_CSP_MODE MODE_YUV
WEBP_CSP_MODE MODE_YUVA #yuv 4:2:0


ctypedef struct WebPDecoderOptions:
int bypass_filtering #if true, skip the in-loop filtering
int no_fancy_upsampling #if true, use faster pointwise upsampler
int use_cropping #if true, cropping is applied _first_
int crop_left
int crop_top #top-left position for cropping.
#Will be snapped to even values.
int crop_width
int crop_height #dimension of the cropping area
int use_scaling #if true, scaling is applied _afterward_
int scaled_width, scaled_height #final resolution
int use_threads #if true, use multi-threaded decoding

int force_rotation #forced rotation (to be applied _last_)
int no_enhancement #if true, discard enhancement layer
uint32_t pad[6] #padding for later use

ctypedef struct WebPBitstreamFeatures:
int width #Width in pixels, as read from the bitstream.
int height #Height in pixels, as read from the bitstream.
int has_alpha #True if the bitstream contains an alpha channel.
int has_animation #True if the bitstream is an animation.
#Unused for now:
int bitstream_version #should be 0 for now. TODO(later)
int no_incremental_decoding #if true, using incremental decoding is not recommended.
int rotate #TODO(later)
int uv_sampling #should be 0 for now. TODO(later)
uint32_t pad[2] #padding for later use

ctypedef struct WebPRGBABuffer: #view as RGBA
uint8_t* rgba #pointer to RGBA samples
int stride #stride in bytes from one scanline to the next.
size_t size #total size of the *rgba buffer.

ctypedef struct WebPYUVABuffer: #view as YUVA
uint8_t* y #pointer to luma
uint8_t* u #pointer to chroma U
uint8_t* v #pointer to chroma V
uint8_t* a #pointer to alpha samples
int y_stride #luma stride
int u_stride, v_stride #chroma strides
int a_stride #alpha stride
size_t y_size #luma plane size
size_t u_size, v_size #chroma planes size
size_t a_size #alpha-plane size

ctypedef struct u:
WebPRGBABuffer RGBA
WebPYUVABuffer YUVA

ctypedef struct WebPDecBuffer:
WEBP_CSP_MODE colorspace #Colorspace.
int width, height #Dimensions.
int is_external_memory #If true, 'internal_memory' pointer is not used.
u u
uint32_t pad[4] #padding for later use
uint8_t* private_memory #Internally allocated memory (only when
#is_external_memory is false). Should not be used
#externally, but accessed via the buffer union.

ctypedef struct WebPDecoderConfig:
WebPBitstreamFeatures input #Immutable bitstream features (optional)
WebPDecBuffer output #Output buffer (can point to external mem)
WebPDecoderOptions options #Decoding options


VP8StatusCode WebPGetFeatures(const uint8_t* data, size_t data_size,
WebPBitstreamFeatures* features)

int WebPInitDecoderConfig(WebPDecoderConfig* config)
VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size,
WebPDecoderConfig* config)


ERROR_TO_NAME = {
#VP8_STATUS_OK
VP8_STATUS_OUT_OF_MEMORY : "out of memory",
VP8_STATUS_INVALID_PARAM : "invalid parameter",
VP8_STATUS_BITSTREAM_ERROR : "bitstream error",
VP8_STATUS_UNSUPPORTED_FEATURE : "unsupported feature",
VP8_STATUS_SUSPENDED : "suspended",
VP8_STATUS_USER_ABORT : "user abort",
VP8_STATUS_NOT_ENOUGH_DATA : "not enough data",
}

def get_version():
version = WebPGetDecoderVersion()
return (version >> 16) & 0xff, (version >> 8) & 0xff, version & 0xff

def webp_check(int ret):
if ret==0:
return
err = ERROR_TO_NAME.get(ret, ret)
raise Exception("error: %s" % err)


cdef class WebpBufferWrapper:
"""
Opaque object wrapping the buffer,
calling free will free the underlying memory.
"""

cdef unsigned long buffer_ptr
cdef size_t size

def __cinit__(self, unsigned long buffer_ptr, size_t size):
self.buffer_ptr = buffer_ptr
self.size = size

def __del__(self):
assert self.buffer_ptr==0, "WebpBufferWrapper out of scope before being freed!"

def get_pixels(self):
assert self.buffer_ptr>0, "WebpBufferWrapper has already been freed!"
return memory_as_pybuffer(<void *> self.buffer_ptr, self.size, False)

def free(self): #@DuplicatedSignature
if self.buffer_ptr!=0:
free(<void *>self.buffer_ptr)
self.buffer_ptr = 0


def decompress(data, has_alpha):
"""
This returns a WebpBufferWrapper, you MUST call free() on it
once the pixel buffer can be freed.
"""
cdef WebPDecoderConfig config
config.options.use_threads = 1
WebPInitDecoderConfig(&config)
webp_check(WebPGetFeatures(data, len(data), &config.input))
log("webp decompress found features: width=%s, height=%s, has_alpha=%s", config.input.width, config.input.height, config.input.has_alpha)

cdef int stride = 4 * config.input.width
if has_alpha:
rgb_format = "BGRA"
config.output.colorspace = MODE_bgrA
else:
rgb_format = "RGB"
config.output.colorspace = MODE_RGB
cdef size_t size = stride * config.input.height
#allocate the buffer:
cdef uint8_t *buffer = <uint8_t*> xmemalign(size + stride) #add one line of padding
cdef WebpBufferWrapper b = WebpBufferWrapper(<unsigned long> buffer, size)
config.output.u.RGBA.rgba = buffer
config.output.u.RGBA.stride = stride
config.output.u.RGBA.size = size
config.output.is_external_memory = 1

webp_check(WebPDecode(data, len(data), &config))

return b, config.input.width, config.input.height, stride, has_alpha and config.input.has_alpha, rgb_format

0 comments on commit 7630cd0

Please sign in to comment.