<commit_before>// Copyright (C) 2016  ParadoxSpiral
//
// This file is part of mpv-rs.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

#![allow(dead_code, improper_ctypes, missing_docs, non_camel_case_types)]
use libc;

// Used in basically every API call.
pub enum MpvHandle {}

enum_from_primitive! {
    #[repr(C)]
    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    pub enum MpvError {
        Success = 0,
        EventQueueFull = -1,
        NoMem = -2,
        Uninitialized = -3,
        InvalidParameter = -4,
        OptionNotFound = -5,
        OptionFormat = -6,
        OptionError = -7,
        PropertyNotFound = -8,
        PropertyFormat = -9,
        PropertyUnavailable = -10,
        PropertyError = -11,
        Command = -12,
        LoadingFailed = -13,
        AoInitFailed = -14,
        VoInitFailed = -15,
        NothingToPlay = -16,
        UnknownFormat = -17,
        Unsupported = -18,
        NotImplemented = -19,
        Generic = -20,
    }
}

#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MpvFormat {
    None = 0,
    String = 1,
    OsdString = 2,
    Flag = 3,
    Int64 = 4,
    Double = 5,
    Node = 6,
    NodeArray = 7,
    NodeMap = 8,
    ByteArray = 9,
}

#[repr(C)]
pub union NodeUnion {
    pub _char: *mut libc::c_char,
    pub flag: libc::c_int,
    pub int64: libc::int64_t,
    pub double: libc::c_double,
    pub list: *mut MpvNodeList,
    pub ba: *mut MpvByteArray,
}

#[repr(C)]
pub struct MpvNode {
    pub u: NodeUnion,
    pub format: MpvFormat,
}

#[repr(C)]
pub struct MpvNodeList {
    pub num: libc::c_int,
    pub values: *mut MpvNode,
    pub keys: *mut *mut libc::c_char,
}

#[repr(C)]
#[derive(Debug)]
pub struct MpvByteArray {
    pub data: *mut libc::c_void,
    pub size: libc::size_t,
}

#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MpvEventId {
    None = 0,
    Shutdown = 1,
    LogMessage = 2,
    GetPropertyReply = 3,
    SetPropertyReply = 4,
    CommandReply = 5,
    StartFile = 6,
    EndFile = 7,
    FileLoaded = 8,
    TracksChanged = 9,
    TrackSwitched = 10,
    Idle = 11,
    Pause = 12,
    Unpause = 13,
    Tick = 14,
    ScriptInputDispatch = 15,
    ClientMessage = 16,
    VideoReconfig = 17,
    AudioReconfig = 18,
    MetadataUpdate = 19,
    Seek = 20,
    PlaybackRestart = 21,
    PropertyChange = 22,
    ChapterChange = 23,
    QueueOverflow = 24,
}

#[repr(C)]
#[derive(Debug)]
pub struct MpvEventProperty {
    pub name: *const libc::c_char,
    pub format: MpvFormat,
    pub data: *mut libc::c_void,
}

#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MpvLogLevel {
    None = 0,
    Fatal = 10,
    Error = 20,
    Warn = 30,
    Info = 40,
    V = 50,
    Debug = 60,
    Trace = 70,
}

#[repr(C)]
pub struct MpvEventLogMessage {
    pub prefix: *const libc::c_char,
    pub level: *const libc::c_char,
    pub text: *const libc::c_char,
    pub log_level: MpvLogLevel,
}

#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MpvEndFileReason {
    Eof = 0,
    Stop = 2,
    Quit = 3,
    Error = 4,
    Redirect = 5,
}

#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct MpvEventEndFile {
    pub reason: libc::c_int,
    pub error: libc::c_int,
}

#[repr(C)]
pub struct MpvEventScriptInputDispatch {
    pub arg0: libc::c_int,
    pub type_: *const libc::c_char,
}

#[repr(C)]
pub struct MpvEventClientMessage {
    pub num_args: libc::c_int,
    pub args: *mut *const libc::c_char,
}

#[repr(C)]
pub struct MpvEvent {
    pub event_id: MpvEventId,
    pub error: libc::c_int,
    pub reply_userdata: libc::uint64_t,
    pub data: *mut libc::c_void,
}

#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MpvSubApi {
    OpenglCb = 1,
}

pub type mpv_stream_cb_read_fn = unsafe extern "C" fn(cookie: *mut libc::c_void,
                                                      buf: *mut libc::c_char,
                                                      nbytes: libc::uint64_t) -> libc::int64_t;
pub type mpv_stream_cb_seek_fn = unsafe extern "C" fn(cookie: *mut libc::c_void,
                                                      offset: libc::int64_t) -> libc::int64_t;
pub type mpv_stream_cb_size_fn = unsafe extern "C" fn(cookie: *mut libc::c_void) -> libc::int64_t;
pub type mpv_stream_cb_close_fn = unsafe extern "C" fn(cookie: *mut libc::c_void);

pub type mpv_stream_cb_open_ro_fn = unsafe extern "C" fn(user_data: *mut libc::c_void,
                                                         uri: *mut libc::c_char,
                                                         info: *mut MpvStreamCbInfo)
                                                         -> libc::c_int;

#[repr(C)]
pub struct MpvStreamCbInfo {
    pub cookie: *mut libc::c_void,

    pub read_fn: mpv_stream_cb_read_fn,
    pub seek_fn: mpv_stream_cb_seek_fn,
    pub size_fn: mpv_stream_cb_size_fn,
    pub close_fn: mpv_stream_cb_close_fn,
}

#[cfg_attr(feature="static", link(name = "mpv", kind = "static"))]
#[cfg_attr(not(feature="static"), link(name = "mpv"))]
extern "C" {
    pub fn mpv_client_api_version() -> u32;
    pub fn mpv_error_string(error: libc::c_int) -> *const libc::c_char;
    pub fn mpv_free(data: *mut libc::c_void);
    pub fn mpv_client_name(ctx: *const MpvHandle) -> *const libc::c_char;
    pub fn mpv_create() -> *mut MpvHandle;
    pub fn mpv_initialize(ctx: *mut MpvHandle) -> libc::c_int;
    pub fn mpv_detach_destroy(ctx: *mut MpvHandle);
    pub fn mpv_terminate_destroy(ctx: *mut MpvHandle);
    pub fn mpv_create_client(ctx: *mut MpvHandle,
                             name: *const libc::c_char)
                             -> *mut MpvHandle;
    pub fn mpv_load_config_file(ctx: *mut MpvHandle,
                                filename: *const libc::c_char)
                                -> libc::c_int;
    pub fn mpv_suspend(ctx: *mut MpvHandle);
    pub fn mpv_resume(ctx: *mut MpvHandle);
    pub fn mpv_free_node_contents(node: *mut MpvNode);
    pub fn mpv_set_option(ctx: *mut MpvHandle,
                          name: *const libc::c_char,
                          format: libc::c_int,
                          data: *mut libc::c_void)
                          -> libc::c_int;
    pub fn mpv_set_option_string(ctx: *mut MpvHandle,
                                 name: *const libc::c_char,
                                 data: *const libc::c_char)
                                 -> libc::c_int;
    pub fn mpv_command(ctx: *mut MpvHandle,
                       args: *mut *const libc::c_char)
                       -> libc::c_int;
    pub fn mpv_command_node(ctx: *mut MpvHandle,
                            args: *mut MpvNode,
                            result: *mut MpvNode)
                            -> libc::c_int;
    pub fn mpv_command_string(ctx: *mut MpvHandle,
                              args: *const libc::c_char)
                              -> libc::c_int;
    pub fn mpv_get_time_us(ctx: *mut MpvHandle) -> libc::int64_t;
    pub fn mpv_command_async(ctx: *mut MpvHandle,
                             reply_userdata: libc::uint64_t,
                             args: *const *const libc::c_char)
                             -> libc::c_int;
    pub fn mpv_set_property(ctx: *mut MpvHandle,
                            name: *const libc::c_char,
                            format: libc::c_int,
                            data: *mut libc::c_void)
                            -> libc::c_int;
    pub fn mpv_set_property_string(ctx: *mut MpvHandle,
                                   name: *const libc::c_char,
                                   data: *const libc::c_char)
                                   -> libc::c_int;
    pub fn mpv_set_property_async(ctx: *mut MpvHandle,
                                  reply_userdata: libc::uint64_t,
                                  name: *const libc::c_char,
                                  format: libc::c_int,
                                  data: *mut libc::c_void)
                                  -> libc::c_int;
    pub fn mpv_get_property(ctx: *mut MpvHandle,
                            name: *const libc::c_char,
                            format: libc::c_int,
                            data: *mut libc::c_void)
                            -> libc::c_int;
    pub fn mpv_get_property_string(ctx: *mut MpvHandle,
                                   name: *const libc::c_char)
                                   -> *mut libc::c_char;
    pub fn mpv_get_property_osd_string(ctx: *mut MpvHandle,
                                       name: *const libc::c_char)
                                       -> *mut libc::c_char;
    pub fn mpv_get_property_async(ctx: *mut MpvHandle,
                                  reply_userdata: libc::uint64_t,
                                  name: *const libc::c_char,
                                  format: libc::c_int)
                                  -> libc::c_int;
    pub fn mpv_observe_property(mpv: *mut MpvHandle,
                                reply_userdata: libc::uint64_t,
                                name: *const libc::c_char,
                                format: libc::c_int)
                                -> libc::c_int;
    pub fn mpv_unobserve_property(mpv: *mut MpvHandle,
                                  registered_reply_userdata: libc::uint64_t)
                                  -> libc::c_int;
    pub fn mpv_event_name(event: MpvEventId) -> *const libc::c_char;
    pub fn mpv_request_event(ctx: *mut MpvHandle,
                             event: MpvEventId,
                             enable: libc::c_int)
                             -> libc::c_int;
    pub fn mpv_request_log_messages(ctx: *mut MpvHandle,
                                    min_level: *const libc::c_char)
                                    -> libc::c_int;
    pub fn mpv_wait_event(ctx: *mut MpvHandle,
                          timeout: libc::c_double)
                          -> *mut MpvEvent;
    pub fn mpv_wakeup(ctx: *mut MpvHandle);
    pub fn mpv_set_wakeup_callback(ctx: *mut MpvHandle,
                                   cb: unsafe extern "C" fn(*mut libc::c_void),
                                   d: *mut libc::c_void);
    pub fn mpv_get_wakeup_pipe(ctx: *mut MpvHandle) -> libc::c_int;
    pub fn mpv_wait_async_requests(ctx: *mut MpvHandle);
    pub fn mpv_get_sub_api(ctx: *mut MpvHandle, sub_api: MpvSubApi);
    pub fn mpv_stream_cb_add_ro(ctx: *mut MpvHandle,
                                protocol: *const libc::c_char,
                                user_data: *mut libc::c_void,
                                open_fn: mpv_stream_cb_open_ro_fn)
                                -> libc::c_int;
}
<commit_msg>[raw] Add primitives for opengl callback<commit_after>// Copyright (C) 2016  ParadoxSpiral
//
// This file is part of mpv-rs.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

#![allow(dead_code, improper_ctypes, missing_docs, non_camel_case_types)]
use libc;

// Used in basically every API call.
pub enum MpvHandle {}

// Used for opengl callback.
pub enum MpvOpenGlCbContext {}

enum_from_primitive! {
    #[repr(C)]
    #[derive(Clone, Copy, Debug, PartialEq, Eq)]
    pub enum MpvError {
        Success = 0,
        EventQueueFull = -1,
        NoMem = -2,
        Uninitialized = -3,
        InvalidParameter = -4,
        OptionNotFound = -5,
        OptionFormat = -6,
        OptionError = -7,
        PropertyNotFound = -8,
        PropertyFormat = -9,
        PropertyUnavailable = -10,
        PropertyError = -11,
        Command = -12,
        LoadingFailed = -13,
        AoInitFailed = -14,
        VoInitFailed = -15,
        NothingToPlay = -16,
        UnknownFormat = -17,
        Unsupported = -18,
        NotImplemented = -19,
        Generic = -20,
    }
}

#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MpvFormat {
    None = 0,
    String = 1,
    OsdString = 2,
    Flag = 3,
    Int64 = 4,
    Double = 5,
    Node = 6,
    NodeArray = 7,
    NodeMap = 8,
    ByteArray = 9,
}

#[repr(C)]
pub union NodeUnion {
    pub _char: *mut libc::c_char,
    pub flag: libc::c_int,
    pub int64: libc::int64_t,
    pub double: libc::c_double,
    pub list: *mut MpvNodeList,
    pub ba: *mut MpvByteArray,
}

#[repr(C)]
pub struct MpvNode {
    pub u: NodeUnion,
    pub format: MpvFormat,
}

#[repr(C)]
pub struct MpvNodeList {
    pub num: libc::c_int,
    pub values: *mut MpvNode,
    pub keys: *mut *mut libc::c_char,
}

#[repr(C)]
#[derive(Debug)]
pub struct MpvByteArray {
    pub data: *mut libc::c_void,
    pub size: libc::size_t,
}

#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MpvEventId {
    None = 0,
    Shutdown = 1,
    LogMessage = 2,
    GetPropertyReply = 3,
    SetPropertyReply = 4,
    CommandReply = 5,
    StartFile = 6,
    EndFile = 7,
    FileLoaded = 8,
    TracksChanged = 9,
    TrackSwitched = 10,
    Idle = 11,
    Pause = 12,
    Unpause = 13,
    Tick = 14,
    ScriptInputDispatch = 15,
    ClientMessage = 16,
    VideoReconfig = 17,
    AudioReconfig = 18,
    MetadataUpdate = 19,
    Seek = 20,
    PlaybackRestart = 21,
    PropertyChange = 22,
    ChapterChange = 23,
    QueueOverflow = 24,
}

#[repr(C)]
#[derive(Debug)]
pub struct MpvEventProperty {
    pub name: *const libc::c_char,
    pub format: MpvFormat,
    pub data: *mut libc::c_void,
}

#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MpvLogLevel {
    None = 0,
    Fatal = 10,
    Error = 20,
    Warn = 30,
    Info = 40,
    V = 50,
    Debug = 60,
    Trace = 70,
}

#[repr(C)]
pub struct MpvEventLogMessage {
    pub prefix: *const libc::c_char,
    pub level: *const libc::c_char,
    pub text: *const libc::c_char,
    pub log_level: MpvLogLevel,
}

#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MpvEndFileReason {
    Eof = 0,
    Stop = 2,
    Quit = 3,
    Error = 4,
    Redirect = 5,
}

#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct MpvEventEndFile {
    pub reason: libc::c_int,
    pub error: libc::c_int,
}

#[repr(C)]
pub struct MpvEventScriptInputDispatch {
    pub arg0: libc::c_int,
    pub type_: *const libc::c_char,
}

#[repr(C)]
pub struct MpvEventClientMessage {
    pub num_args: libc::c_int,
    pub args: *mut *const libc::c_char,
}

#[repr(C)]
pub struct MpvEvent {
    pub event_id: MpvEventId,
    pub error: libc::c_int,
    pub reply_userdata: libc::uint64_t,
    pub data: *mut libc::c_void,
}

#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MpvSubApi {
    OpenglCb = 1,
}

pub type mpv_stream_cb_read_fn = unsafe extern "C" fn(cookie: *mut libc::c_void,
                                                      buf: *mut libc::c_char,
                                                      nbytes: libc::uint64_t) -> libc::int64_t;
pub type mpv_stream_cb_seek_fn = unsafe extern "C" fn(cookie: *mut libc::c_void,
                                                      offset: libc::int64_t) -> libc::int64_t;
pub type mpv_stream_cb_size_fn = unsafe extern "C" fn(cookie: *mut libc::c_void) -> libc::int64_t;
pub type mpv_stream_cb_close_fn = unsafe extern "C" fn(cookie: *mut libc::c_void);

pub type mpv_stream_cb_open_ro_fn = unsafe extern "C" fn(user_data: *mut libc::c_void,
                                                         uri: *mut libc::c_char,
                                                         info: *mut MpvStreamCbInfo)
                                                         -> libc::c_int;

pub type mpv_opengl_cb_update_fn = unsafe extern "C" fn(cb_ctx: *mut libc::c_void);
pub type mpv_opengl_cb_get_proc_address_fn = unsafe extern "C" fn(fn_ctx: *mut libc::c_void,
                                                                  name: *const libc::c_char,
                                                                  info: *mut MpvStreamCbInfo);

#[repr(C)]
pub struct MpvStreamCbInfo {
    pub cookie: *mut libc::c_void,

    pub read_fn: mpv_stream_cb_read_fn,
    pub seek_fn: mpv_stream_cb_seek_fn,
    pub size_fn: mpv_stream_cb_size_fn,
    pub close_fn: mpv_stream_cb_close_fn,
}

#[cfg_attr(feature="static", link(name = "mpv", kind = "static"))]
#[cfg_attr(not(feature="static"), link(name = "mpv"))]
extern "C" {
    pub fn mpv_client_api_version() -> u32;
    pub fn mpv_error_string(error: libc::c_int) -> *const libc::c_char;
    pub fn mpv_free(data: *mut libc::c_void);
    pub fn mpv_client_name(ctx: *const MpvHandle) -> *const libc::c_char;
    pub fn mpv_create() -> *mut MpvHandle;
    pub fn mpv_initialize(ctx: *mut MpvHandle) -> libc::c_int;
    pub fn mpv_detach_destroy(ctx: *mut MpvHandle);
    pub fn mpv_terminate_destroy(ctx: *mut MpvHandle);
    pub fn mpv_create_client(ctx: *mut MpvHandle,
                             name: *const libc::c_char)
                             -> *mut MpvHandle;
    pub fn mpv_load_config_file(ctx: *mut MpvHandle,
                                filename: *const libc::c_char)
                                -> libc::c_int;
    pub fn mpv_suspend(ctx: *mut MpvHandle);
    pub fn mpv_resume(ctx: *mut MpvHandle);
    pub fn mpv_free_node_contents(node: *mut MpvNode);
    pub fn mpv_set_option(ctx: *mut MpvHandle,
                          name: *const libc::c_char,
                          format: libc::c_int,
                          data: *mut libc::c_void)
                          -> libc::c_int;
    pub fn mpv_set_option_string(ctx: *mut MpvHandle,
                                 name: *const libc::c_char,
                                 data: *const libc::c_char)
                                 -> libc::c_int;
    pub fn mpv_command(ctx: *mut MpvHandle,
                       args: *mut *const libc::c_char)
                       -> libc::c_int;
    pub fn mpv_command_node(ctx: *mut MpvHandle,
                            args: *mut MpvNode,
                            result: *mut MpvNode)
                            -> libc::c_int;
    pub fn mpv_command_string(ctx: *mut MpvHandle,
                              args: *const libc::c_char)
                              -> libc::c_int;
    pub fn mpv_get_time_us(ctx: *mut MpvHandle) -> libc::int64_t;
    pub fn mpv_command_async(ctx: *mut MpvHandle,
                             reply_userdata: libc::uint64_t,
                             args: *const *const libc::c_char)
                             -> libc::c_int;
    pub fn mpv_set_property(ctx: *mut MpvHandle,
                            name: *const libc::c_char,
                            format: libc::c_int,
                            data: *mut libc::c_void)
                            -> libc::c_int;
    pub fn mpv_set_property_string(ctx: *mut MpvHandle,
                                   name: *const libc::c_char,
                                   data: *const libc::c_char)
                                   -> libc::c_int;
    pub fn mpv_set_property_async(ctx: *mut MpvHandle,
                                  reply_userdata: libc::uint64_t,
                                  name: *const libc::c_char,
                                  format: libc::c_int,
                                  data: *mut libc::c_void)
                                  -> libc::c_int;
    pub fn mpv_get_property(ctx: *mut MpvHandle,
                            name: *const libc::c_char,
                            format: libc::c_int,
                            data: *mut libc::c_void)
                            -> libc::c_int;
    pub fn mpv_get_property_string(ctx: *mut MpvHandle,
                                   name: *const libc::c_char)
                                   -> *mut libc::c_char;
    pub fn mpv_get_property_osd_string(ctx: *mut MpvHandle,
                                       name: *const libc::c_char)
                                       -> *mut libc::c_char;
    pub fn mpv_get_property_async(ctx: *mut MpvHandle,
                                  reply_userdata: libc::uint64_t,
                                  name: *const libc::c_char,
                                  format: libc::c_int)
                                  -> libc::c_int;
    pub fn mpv_observe_property(mpv: *mut MpvHandle,
                                reply_userdata: libc::uint64_t,
                                name: *const libc::c_char,
                                format: libc::c_int)
                                -> libc::c_int;
    pub fn mpv_unobserve_property(mpv: *mut MpvHandle,
                                  registered_reply_userdata: libc::uint64_t)
                                  -> libc::c_int;
    pub fn mpv_event_name(event: MpvEventId) -> *const libc::c_char;
    pub fn mpv_request_event(ctx: *mut MpvHandle,
                             event: MpvEventId,
                             enable: libc::c_int)
                             -> libc::c_int;
    pub fn mpv_request_log_messages(ctx: *mut MpvHandle,
                                    min_level: *const libc::c_char)
                                    -> libc::c_int;
    pub fn mpv_wait_event(ctx: *mut MpvHandle,
                          timeout: libc::c_double)
                          -> *mut MpvEvent;
    pub fn mpv_wakeup(ctx: *mut MpvHandle);
    pub fn mpv_set_wakeup_callback(ctx: *mut MpvHandle,
                                   cb: unsafe extern "C" fn(*mut libc::c_void),
                                   d: *mut libc::c_void);
    pub fn mpv_get_wakeup_pipe(ctx: *mut MpvHandle) -> libc::c_int;
    pub fn mpv_wait_async_requests(ctx: *mut MpvHandle);
    pub fn mpv_get_sub_api(ctx: *mut MpvHandle, sub_api: MpvSubApi);
    pub fn mpv_stream_cb_add_ro(ctx: *mut MpvHandle,
                                protocol: *const libc::c_char,
                                user_data: *mut libc::c_void,
                                open_fn: mpv_stream_cb_open_ro_fn)
                                -> libc::c_int;
    pub fn mpv_opengl_cb_set_update_callback(ctx: *mut MpvOpenGlCbContext,
                                    callback: mpv_opengl_cb_update_fn,
                                    callback_ctx: *mut libc::c_void);
    pub fn mpv_opengl_cb_init_gl(ctx: *mut MpvOpenGlCbContext,
                                 exts: *const libc::c_char,
                                 get_proc_address: mpv_opengl_cb_get_proc_address_fn,
                                 get_proc_address_ctx: *mut libc::c_void)
                                 -> libc::c_int;
    pub fn mpv_opengl_cb_draw(ctx: *mut MpvOpenGlCbContext,
                              fbo: libc::c_int,
                              w: libc::c_int,
                              h: libc::c_int,)
                              -> libc::c_int;
    pub fn mpv_opengl_cb_render(ctx: *mut MpvOpenGlCbContext,
                                fbo: libc::c_int,
                                vp: [libc::c_int; 4])
                                -> libc::c_int;
    pub fn mpv_opengl_cb_report_flip(ctx: *mut MpvOpenGlCbContext,
                                     time: libc::int64_t)
                                     -> libc::c_int;
    pub fn mpv_opengl_cb_uninit_gl(ctx: *mut MpvOpenGlCbContext)
                                   -> libc::c_int;
}
<|endoftext|>


<commit_before>//! Types for the *m.receipt* event.

use std::{collections::BTreeMap, time::SystemTime};

use ruma_events_macros::ruma_event;
use ruma_identifiers::{EventId, RoomId, UserId};
use serde::{Deserialize, Serialize};

ruma_event! {
    /// Informs the client of new receipts.
    ReceiptEvent {
        kind: Event,
        event_type: "m.receipt",
        fields: {
            /// The unique identifier for the room associated with this event.
            ///
            /// `None` if the room is known through other means (such as this even being part of an
            /// event list scoped to a room in a `/sync` response)
            pub room_id: Option<RoomId>,
        },
        content_type_alias: {
            /// The payload for `ReceiptEvent`.
            ///
            /// A mapping of event ID to a collection of receipts for this event ID. The event ID is the ID of
            /// the event being acknowledged and *not* an ID for the receipt itself.
            BTreeMap<EventId, Receipts>
        },
    }
}

/// A collection of receipts.
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct Receipts {
    /// A collection of users who have sent *m.read* receipts for this event.
    #[serde(rename = "m.read")]
    #[serde(default)]
    pub read: Option<UserReceipts>,
}

/// A mapping of user ID to receipt.
///
/// The user ID is the entity who sent this receipt.
pub type UserReceipts = BTreeMap<UserId, Receipt>;

/// An acknowledgement of an event.
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct Receipt {
    /// The time when the receipt was sent.
    #[serde(
        with = "ruma_serde::time::opt_ms_since_unix_epoch",
        default,
        skip_serializing_if = "Option::is_none"
    )]
    pub ts: Option<SystemTime>,
}
<commit_msg>Update serde attrs in receipt<commit_after>//! Types for the *m.receipt* event.

use std::{collections::BTreeMap, time::SystemTime};

use ruma_events_macros::ruma_event;
use ruma_identifiers::{EventId, RoomId, UserId};
use serde::{Deserialize, Serialize};

ruma_event! {
    /// Informs the client of new receipts.
    ReceiptEvent {
        kind: Event,
        event_type: "m.receipt",
        fields: {
            /// The unique identifier for the room associated with this event.
            ///
            /// `None` if the room is known through other means (such as this even being part of an
            /// event list scoped to a room in a `/sync` response)
            pub room_id: Option<RoomId>,
        },
        content_type_alias: {
            /// The payload for `ReceiptEvent`.
            ///
            /// A mapping of event ID to a collection of receipts for this event ID. The event ID is the ID of
            /// the event being acknowledged and *not* an ID for the receipt itself.
            BTreeMap<EventId, Receipts>
        },
    }
}

/// A collection of receipts.
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct Receipts {
    /// A collection of users who have sent *m.read* receipts for this event.
    #[serde(default, rename = "m.read")]
    pub read: Option<UserReceipts>,
}

/// A mapping of user ID to receipt.
///
/// The user ID is the entity who sent this receipt.
pub type UserReceipts = BTreeMap<UserId, Receipt>;

/// An acknowledgement of an event.
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct Receipt {
    /// The time when the receipt was sent.
    #[serde(
        with = "ruma_serde::time::opt_ms_since_unix_epoch",
        default,
        skip_serializing_if = "Option::is_none"
    )]
    pub ts: Option<SystemTime>,
}
<|endoftext|>


<commit_before>//! AWS API requests.
//!
//! Wraps the Hyper library to send PUT, POST, DELETE and GET requests.

extern crate lazy_static;

use std::env;
use std::io::Read;
use std::io::Error as IoError;
use std::error::Error;
use std::fmt;
use std::collections::HashMap;

use hyper::Client;
use hyper::Error as HyperError;
use hyper::header::{Headers, UserAgent, ContentType};
use hyper::mime::{Mime, TopLevel, SubLevel};
use hyper::status::StatusCode;
use hyper::method::Method;

use log::LogLevel::Debug;

use signature::SignedRequest;

// Pulls in the statically generated rustc version.
include!(concat!(env!("OUT_DIR"), "/user_agent_vars.rs"));

// Use a lazy static to cache the default User-Agent header
// because it never changes once it's been computed.
lazy_static! {
    static ref DEFAULT_USER_AGENT: Vec<Vec<u8>> = vec![format!("rusoto/{} rust/{} {}",
            env!("CARGO_PKG_VERSION"), RUST_VERSION, env::consts::OS).as_bytes().to_vec()];
}

#[derive(Clone)]
pub struct HttpResponse {
    pub status: StatusCode,
    pub body: String,
    pub raw_body: Vec<u8>,
    pub headers: HashMap<String, String>
}

#[derive(Debug, PartialEq)]
pub struct HttpDispatchError {
    message: String
}

impl Error for HttpDispatchError {
    fn description(&self) -> &str {
        &self.message
    }
}

impl fmt::Display for HttpDispatchError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.message)
    }
}

impl From<HyperError> for HttpDispatchError {
    fn from(err: HyperError) -> HttpDispatchError {
        HttpDispatchError { message: err.description().to_string() }
    }
}

impl From<IoError> for HttpDispatchError {
    fn from(err: IoError) -> HttpDispatchError {
        HttpDispatchError { message: err.description().to_string() }
    }
}

pub trait DispatchSignedRequest {
    fn dispatch(&self, request: &SignedRequest) -> Result<HttpResponse, HttpDispatchError>;
}

impl DispatchSignedRequest for Client {
    fn dispatch(&self, request: &SignedRequest) -> Result<HttpResponse, HttpDispatchError> {
        let hyper_method = match request.method().as_ref() {
            "POST" => Method::Post,
            "PUT" => Method::Put,
            "DELETE" => Method::Delete,
            "GET" => Method::Get,
            "HEAD" => Method::Head,
            v => return Err(HttpDispatchError { message: format!("Unsupported HTTP verb {}", v) })
        };

        // translate the headers map to a format Hyper likes
        let mut hyper_headers = Headers::new();
        for h in request.headers().iter() {
            hyper_headers.set_raw(h.0.to_owned(), h.1.to_owned());
        }

        // Add a default user-agent header if one is not already present.
        if !hyper_headers.has::<UserAgent>() {
            hyper_headers.set_raw("user-agent".to_owned(), DEFAULT_USER_AGENT.clone());
        }

        let mut final_uri = format!("https://{}{}", request.hostname(), request.canonical_path());
        if !request.canonical_query_string().is_empty() {
            final_uri = final_uri + &format!("?{}", request.canonical_query_string());
        }

        if log_enabled!(Debug) {
            let payload = request.payload().map(|mut payload_bytes| {
                let mut payload_string = String::new();

                payload_bytes.read_to_string(&mut payload_string)
                    .map(|_| payload_string)
                    .unwrap_or_else(|_| String::from("<non-UTF-8 data>"))
            });

            debug!("Full request: \n method: {}\n final_uri: {}\n payload: {}\nHeaders:\n", hyper_method, final_uri, payload.unwrap_or("".to_owned()));
            for h in hyper_headers.iter() {
                debug!("{}:{}", h.name(), h.value_string());
            }
        }
        let mut hyper_response = match request.payload() {
            None => try!(self.request(hyper_method, &final_uri).headers(hyper_headers).body("").send()),
            Some(payload_contents) => try!(self.request(hyper_method, &final_uri).headers(hyper_headers).body(payload_contents).send()),
        };
        let mut body = String::new();
        let mut body_as_bytes : Vec<u8> = Vec::new();
        // TODO: don't create new content type every request:
        let response_is_binary = match hyper_response.headers.get::<ContentType>() {
            Some(content_type) => content_type.eq(&ContentType(Mime(TopLevel::Application, SubLevel::OctetStream, Vec::new()))),
            None => false,
        };
        match response_is_binary {
            true => try!(hyper_response.read_to_end(&mut body_as_bytes)),
            false => try!(hyper_response.read_to_string(&mut body)),
        };

        if log_enabled!(Debug) {
            debug!("Response body:\n{}", body);
            debug!("Response raw_body:\n{:?}", body_as_bytes);
        }

        let mut headers: HashMap<String, String> = HashMap::new();

        for header in hyper_response.headers.iter() {
            headers.insert(header.name().to_string(), header.value_string());
        }

        Ok(HttpResponse {
            status: hyper_response.status.clone(),
            body: body,
            raw_body: body_as_bytes,
            headers: headers
        })

    }
}
<commit_msg>Uses a lazy static for binary content type request checking.<commit_after>//! AWS API requests.
//!
//! Wraps the Hyper library to send PUT, POST, DELETE and GET requests.

extern crate lazy_static;

use std::env;
use std::io::Read;
use std::io::Error as IoError;
use std::error::Error;
use std::fmt;
use std::collections::HashMap;

use hyper::Client;
use hyper::Error as HyperError;
use hyper::header::{Headers, UserAgent, ContentType};
use hyper::mime::{Mime, TopLevel, SubLevel};
use hyper::status::StatusCode;
use hyper::method::Method;

use log::LogLevel::Debug;

use signature::SignedRequest;

// Pulls in the statically generated rustc version.
include!(concat!(env!("OUT_DIR"), "/user_agent_vars.rs"));

// Use a lazy static to cache the default User-Agent header
// because it never changes once it's been computed.
lazy_static! {
    static ref DEFAULT_USER_AGENT: Vec<Vec<u8>> = vec![format!("rusoto/{} rust/{} {}",
            env!("CARGO_PKG_VERSION"), RUST_VERSION, env::consts::OS).as_bytes().to_vec()];
}
lazy_static! {
    static ref BINARY_RESPONSE_CONTENTS: ContentType = ContentType(Mime(TopLevel::Application, SubLevel::OctetStream, Vec::new()));
}

#[derive(Clone)]
pub struct HttpResponse {
    pub status: StatusCode,
    pub body: String,
    pub raw_body: Vec<u8>,
    pub headers: HashMap<String, String>
}

#[derive(Debug, PartialEq)]
pub struct HttpDispatchError {
    message: String
}

impl Error for HttpDispatchError {
    fn description(&self) -> &str {
        &self.message
    }
}

impl fmt::Display for HttpDispatchError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.message)
    }
}

impl From<HyperError> for HttpDispatchError {
    fn from(err: HyperError) -> HttpDispatchError {
        HttpDispatchError { message: err.description().to_string() }
    }
}

impl From<IoError> for HttpDispatchError {
    fn from(err: IoError) -> HttpDispatchError {
        HttpDispatchError { message: err.description().to_string() }
    }
}

pub trait DispatchSignedRequest {
    fn dispatch(&self, request: &SignedRequest) -> Result<HttpResponse, HttpDispatchError>;
}

impl DispatchSignedRequest for Client {
    fn dispatch(&self, request: &SignedRequest) -> Result<HttpResponse, HttpDispatchError> {
        let hyper_method = match request.method().as_ref() {
            "POST" => Method::Post,
            "PUT" => Method::Put,
            "DELETE" => Method::Delete,
            "GET" => Method::Get,
            "HEAD" => Method::Head,
            v => return Err(HttpDispatchError { message: format!("Unsupported HTTP verb {}", v) })
        };

        // translate the headers map to a format Hyper likes
        let mut hyper_headers = Headers::new();
        for h in request.headers().iter() {
            hyper_headers.set_raw(h.0.to_owned(), h.1.to_owned());
        }

        // Add a default user-agent header if one is not already present.
        if !hyper_headers.has::<UserAgent>() {
            hyper_headers.set_raw("user-agent".to_owned(), DEFAULT_USER_AGENT.clone());
        }

        let mut final_uri = format!("https://{}{}", request.hostname(), request.canonical_path());
        if !request.canonical_query_string().is_empty() {
            final_uri = final_uri + &format!("?{}", request.canonical_query_string());
        }

        if log_enabled!(Debug) {
            let payload = request.payload().map(|mut payload_bytes| {
                let mut payload_string = String::new();

                payload_bytes.read_to_string(&mut payload_string)
                    .map(|_| payload_string)
                    .unwrap_or_else(|_| String::from("<non-UTF-8 data>"))
            });

            debug!("Full request: \n method: {}\n final_uri: {}\n payload: {}\nHeaders:\n", hyper_method, final_uri, payload.unwrap_or("".to_owned()));
            for h in hyper_headers.iter() {
                debug!("{}:{}", h.name(), h.value_string());
            }
        }
        let mut hyper_response = match request.payload() {
            None => try!(self.request(hyper_method, &final_uri).headers(hyper_headers).body("").send()),
            Some(payload_contents) => try!(self.request(hyper_method, &final_uri).headers(hyper_headers).body(payload_contents).send()),
        };
        let mut body = String::new();
        let mut body_as_bytes : Vec<u8> = Vec::new();
        let response_is_binary = match hyper_response.headers.get::<ContentType>() {
            Some(content_type) => content_type.eq(&BINARY_RESPONSE_CONTENTS),
            None => false,
        };
        match response_is_binary {
            true => try!(hyper_response.read_to_end(&mut body_as_bytes)),
            false => try!(hyper_response.read_to_string(&mut body)),
        };

        if log_enabled!(Debug) {
            debug!("Response body:\n{}", body);
            debug!("Response raw_body:\n{:?}", body_as_bytes);
        }

        let mut headers: HashMap<String, String> = HashMap::new();

        for header in hyper_response.headers.iter() {
            headers.insert(header.name().to_string(), header.value_string());
        }

        Ok(HttpResponse {
            status: hyper_response.status.clone(),
            body: body,
            raw_body: body_as_bytes,
            headers: headers
        })

    }
}
<|endoftext|>


<commit_before>use raft::raftpb::{HardState, ConfState, Entry, Snapshot};
use protobuf;
use raft::errors::Result;
use raft::errors::{Error, StorageError};

#[derive(Debug, Clone)]
pub struct RaftState {
    hard_state: HardState,
    conf_state: ConfState,
}

pub trait Storage {
    fn initial_state(&self) -> Result<RaftState>;
    fn entries(&self, low: u64, high: u64, max_size: u64) -> Result<&[Entry]>;
    fn term(&self, idx: u64) -> Result<u64>;
    fn first_index(&self) -> Result<u64>;
    fn last_index(&self) -> Result<u64>;
    fn snapshot(&self) -> Result<&Snapshot>;
}

pub struct MemStorage {
    hard_state: HardState,
    snapshot: Snapshot,
    entries: Vec<Entry>,
}

impl MemStorage {
    pub fn new() -> MemStorage {
        MemStorage {
            // When starting from scratch populate the list with a dummy entry at term zero.
            entries: vec![Entry::new()],
            hard_state: HardState::new(),
            snapshot: Snapshot::new(),
        }
    }

    pub fn set_hardstate(&mut self, hs: HardState) {
        self.hard_state = hs;
    }

    fn inner_last_index(&self) -> u64 {
        return self.entries[0].get_Index() + self.entries.len() as u64 - 1;
    }

    fn limit_size(entries: &[Entry], max: u64) -> Result<&[Entry]> {
        if entries.len() == 0 {
            return Ok(entries);
        }

        let mut size = protobuf::Message::compute_size(&entries[0]) as u64;
        let mut limit = 1u64;
        while limit < entries.len() as u64 {
            size += protobuf::Message::compute_size(&entries[limit as usize]) as u64;
            if size > max {
                break;
            }
            limit += 1;
        }
        let ents = &entries[..limit as usize];
        Ok(ents)
    }

    fn apply_snapshot(&mut self, snapshot: Snapshot) -> Result<()> {
        let mut e = Entry::new();
        e.set_Term(snapshot.get_metadata().get_term());
        e.set_Index(snapshot.get_metadata().get_index());
        self.entries = vec![e];
        self.snapshot = snapshot;
        Ok(())
    }

    fn create_snapshot(&mut self,
                       idx: u64,
                       cs: Option<ConfState>,
                       data: Vec<u8>)
                       -> Result<&Snapshot> {
        if idx <= self.snapshot.get_metadata().get_index() {
            return Err(Error::Store(StorageError::SnapshotOutOfDate));
        }

        let offset = self.entries[0].get_Index();
        if idx > self.last_index().unwrap() {
            panic!("snapshot {} is out of bound lastindex({})",
                   idx,
                   self.last_index().unwrap())
        }
        self.snapshot.mut_metadata().set_index(idx);
        self.snapshot.mut_metadata().set_term(self.entries[(idx - offset) as usize].get_Term());
        match cs {
            Some(cs) => self.snapshot.mut_metadata().set_conf_state(cs),
            None => {}
        }
        self.snapshot.set_data(data);
        Ok(&self.snapshot)
    }
}

impl Storage for MemStorage {
    fn initial_state(&self) -> Result<RaftState> {
        Ok(RaftState {
            hard_state: self.hard_state.clone(),
            conf_state: self.snapshot.get_metadata().get_conf_state().clone(),
        })
    }

    fn entries(&self, low: u64, high: u64, max_size: u64) -> Result<&[Entry]> {
        let offset = self.entries[0].get_Index();
        if low <= offset {
            return Err(Error::Store(StorageError::Compacted));
        }

        if high > self.inner_last_index() + 1 {
            panic!("index out of bound")
        }
        // only contains dummy entries.
        if self.entries.len() == 1 {
            return Err(Error::Store(StorageError::Unavailable));
        }

        let lo = (low - offset) as usize;
        let hi = (high - offset) as usize;
        let ents: &[Entry] = &self.entries[lo..hi];
        return MemStorage::limit_size(ents, max_size);
    }

    fn term(&self, idx: u64) -> Result<u64> {
        let offset = self.entries[0].get_Index();
        if idx < offset {
            return Err(Error::Store(StorageError::Compacted));
        }
        Ok(self.entries[(idx - offset) as usize].get_Term())
    }

    fn first_index(&self) -> Result<u64> {
        Ok(self.entries[0].get_Index() + 1)
    }

    fn last_index(&self) -> Result<u64> {
        Ok(self.inner_last_index())
    }

    fn snapshot(&self) -> Result<&Snapshot> {
        Ok(&self.snapshot)
    }
}
<commit_msg>Implement compact and append methods<commit_after>
#![feature(vec_push_all)]

use raft::raftpb::{HardState, ConfState, Entry, Snapshot};
use protobuf;
use raft::errors::Result;
use raft::errors::{Error, StorageError};

#[derive(Debug, Clone)]
pub struct RaftState {
    hard_state: HardState,
    conf_state: ConfState,
}

pub trait Storage {
    fn initial_state(&self) -> Result<RaftState>;
    fn entries(&self, low: u64, high: u64, max_size: u64) -> Result<&[Entry]>;
    fn term(&self, idx: u64) -> Result<u64>;
    fn first_index(&self) -> Result<u64>;
    fn last_index(&self) -> Result<u64>;
    fn snapshot(&self) -> Result<&Snapshot>;
}

pub struct MemStorage {
    hard_state: HardState,
    snapshot: Snapshot,
    entries: Vec<Entry>,
}

impl MemStorage {
    pub fn new() -> MemStorage {
        MemStorage {
            // When starting from scratch populate the list with a dummy entry at term zero.
            entries: vec![Entry::new()],
            hard_state: HardState::new(),
            snapshot: Snapshot::new(),
        }
    }

    pub fn set_hardstate(&mut self, hs: HardState) {
        self.hard_state = hs;
    }

    fn inner_last_index(&self) -> u64 {
        return self.entries[0].get_Index() + self.entries.len() as u64 - 1;
    }

    fn limit_size(entries: &[Entry], max: u64) -> Result<&[Entry]> {
        if entries.len() == 0 {
            return Ok(entries);
        }

        let mut size = protobuf::Message::compute_size(&entries[0]) as u64;
        let mut limit = 1u64;
        while limit < entries.len() as u64 {
            size += protobuf::Message::compute_size(&entries[limit as usize]) as u64;
            if size > max {
                break;
            }
            limit += 1;
        }
        let ents = &entries[..limit as usize];
        Ok(ents)
    }

    fn apply_snapshot(&mut self, snapshot: Snapshot) -> Result<()> {
        let mut e = Entry::new();
        e.set_Term(snapshot.get_metadata().get_term());
        e.set_Index(snapshot.get_metadata().get_index());
        self.entries = vec![e];
        self.snapshot = snapshot;
        Ok(())
    }

    fn create_snapshot(&mut self,
                       idx: u64,
                       cs: Option<ConfState>,
                       data: Vec<u8>)
                       -> Result<&Snapshot> {
        if idx <= self.snapshot.get_metadata().get_index() {
            return Err(Error::Store(StorageError::SnapshotOutOfDate));
        }

        let offset = self.entries[0].get_Index();
        if idx > self.last_index().unwrap() {
            panic!("snapshot {} is out of bound lastindex({})",
                   idx,
                   self.last_index().unwrap())
        }
        self.snapshot.mut_metadata().set_index(idx);
        self.snapshot.mut_metadata().set_term(self.entries[(idx - offset) as usize].get_Term());
        match cs {
            Some(cs) => self.snapshot.mut_metadata().set_conf_state(cs),
            None => {}
        }
        self.snapshot.set_data(data);
        Ok(&self.snapshot)
    }

    fn compact(&mut self, compact_index: u64) -> Result<()> {
        let offset = self.entries[0].get_Index();
        if compact_index <= offset {
            return Err(Error::Store(StorageError::Compacted));
        }
        if compact_index > self.last_index().unwrap() {
            panic!("compact {} is out of bound lastindex({})",
                   compact_index,
                   self.last_index().unwrap())
        }

        let i = compact_index - offset;
        let mut old: Vec<Entry> = self.entries[i as usize + 1..].to_vec();
        self.entries = vec![Entry::new()];
        let index = self.entries[i as usize].get_Index();
        let term = self.entries[i as usize].get_Term();
        self.entries[0].set_Index(index);
        self.entries[0].set_Term(term);
        self.entries.append(&mut old);
        Ok(())
    }

    fn append(&mut self, ents: &[Entry]) -> Result<()> {
        if ents.len() == 0 {
            return Ok(());
        }
        let first: u64 = self.entries[0].get_Index() + 1;
        let last: u64 = ents[0].get_Index() + ents.len() as u64 - 1;

        if last < first {
            return Ok(());
        }
        // truncate compacted entries
        let mut te: &[Entry] = ents;
        if first > ents[0].get_Index() {
            let start = (first - ents[0].get_Index()) as usize;
            te = &ents[start..ents.len()];
        }

        let offset = te[0].get_Index() - self.entries[0].get_Index();
        if self.entries.len() as u64 > offset {
            let mut new_entries: Vec<Entry> = vec![Entry::new()];
            new_entries.push_all(&self.entries[..offset as usize]);
            new_entries.push_all(te);
            self.entries = new_entries;
        } else if self.entries.len() as u64 == offset {
            self.entries.push_all(te);
        } else {
            panic!("missing log entry [last: {}, append at: {}]",
                   self.last_index().unwrap(),
                   te[0].get_Index())
        }

        Ok(())
    }
}

impl Storage for MemStorage {
    fn initial_state(&self) -> Result<RaftState> {
        Ok(RaftState {
            hard_state: self.hard_state.clone(),
            conf_state: self.snapshot.get_metadata().get_conf_state().clone(),
        })
    }

    fn entries(&self, low: u64, high: u64, max_size: u64) -> Result<&[Entry]> {
        let offset = self.entries[0].get_Index();
        if low <= offset {
            return Err(Error::Store(StorageError::Compacted));
        }

        if high > self.inner_last_index() + 1 {
            panic!("index out of bound")
        }
        // only contains dummy entries.
        if self.entries.len() == 1 {
            return Err(Error::Store(StorageError::Unavailable));
        }

        let lo = (low - offset) as usize;
        let hi = (high - offset) as usize;
        let ents: &[Entry] = &self.entries[lo..hi];
        return MemStorage::limit_size(ents, max_size);
    }

    fn term(&self, idx: u64) -> Result<u64> {
        let offset = self.entries[0].get_Index();
        if idx < offset {
            return Err(Error::Store(StorageError::Compacted));
        }
        Ok(self.entries[(idx - offset) as usize].get_Term())
    }

    fn first_index(&self) -> Result<u64> {
        Ok(self.entries[0].get_Index() + 1)
    }

    fn last_index(&self) -> Result<u64> {
        Ok(self.inner_last_index())
    }

    fn snapshot(&self) -> Result<&Snapshot> {
        Ok(&self.snapshot)
    }
}
<|endoftext|>


<commit_before>#![doc(html_favicon_url = "https://www.ruma.io/favicon.ico")]
#![doc(html_logo_url = "https://www.ruma.io/images/logo.png")]
//! Types and traits for working with the [Matrix](https://matrix.org) protocol.
//!
//! This crate re-exports things from all of the other ruma crates so you don't
//! have to manually keep all the versions in sync.
//!
//! Which crates are re-exported can be configured through cargo features.
//!
//! > ⚠ Some details might be missing because rustdoc has trouble with re-exports so you may need
//!   to refer to other crates' documentations.
//!
//! # API features
//!
//! Depending on which parts of Matrix are relevant to you, activate the following features:
//!
//! * `appservice-api` -- Application Service API.
//! * `client-api` -- Client-Server API.
//! * `federation-api` -- Server-Server (Federation) API.
//! * `identity-service-api` -- Identity Service API.
//! * `push-gateway-api` -- Push Gateway API.
//!
//! These features have `client`- and `server`-optimized variants that are enabled respectively
//! with the `-c` and `-s` suffixes. For example:
//!   * `client-api-c` -- The Client-Server API optimized for the client side.
//!   * `client-api-s` -- The Client-Server API optimized for the server side.
//!
//! # Compatibility feature
//!
//! * `compat` increases compatibility with other parts of the Matrix ecosystem, at the expense of
//! deviating from the specification.
//!
//! # Convenience features
//!
//! These features are only useful if you want to use a method that requires it:
//!
//! * `either`
//! * `rand`
//! * `markdown`
//!
//! # Unstable features
//!
//! By using these features, you opt out of all semver guarantees Ruma otherwise provides:
//!
//! * `unstable-exhaustive-types` -- Most types in Ruma are marked as non-exhaustive to avoid
//!   breaking changes when new fields are added in the specification. This feature compiles all
//!   types as exhaustive.
//! * `unstable-mscXXXX`, where `XXXX` is the MSC number -- Upcoming Matrix features that may be
//!   subject to change or removal.
//! * `unstable-pre-spec` -- Undocumented Matrix features that may be subject to change or removal.
//!
//! # Common features
//!
//! These submodules are usually activated by the API features when needed:
//!
//! * `api`
//! * `events`
//! * `signatures`
//!
//! # `ruma-client` features
//!
//! The `client` feature activates [`ruma::client`][client], and `client-ext-client-api` activates
//! `ruma-client`s `client-api` feature. All other `client-*` features activate the same feature
//! without the `client-` prefix on `ruma-client`. See the crate's documentation for the effect of
//! these features.
//!
//! If you are viewing this on `docs.rs`, you can have a look at the feature dependencies by
//! clicking **Feature flags** in the toolbar at the top.

#![warn(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]

#[doc(inline)]
pub use ruma_common::serde;

#[cfg(feature = "client")]
#[doc(inline)]
pub use ruma_client as client;
#[cfg(feature = "events")]
#[doc(inline)]
pub use ruma_common::events;
#[cfg(feature = "signatures")]
#[doc(inline)]
pub use ruma_signatures as signatures;
#[cfg(feature = "state-res")]
#[doc(inline)]
pub use ruma_state_res as state_res;

/// (De)serializable types for various [Matrix APIs][apis] requests and responses and abstractions
/// for them.
///
/// [apis]: https://spec.matrix.org/v1.2/#matrix-apis
#[cfg(feature = "api")]
pub mod api {
    pub use ruma_common::api::*;

    #[cfg(feature = "ruma-appservice-api")]
    #[cfg_attr(
        docsrs,
        doc(cfg(any(
            feature = "appservice-api",
            feature = "appservice-api-c",
            feature = "appservice-api-s"
        )))
    )]
    #[doc(inline)]
    pub use ruma_appservice_api as appservice;
    #[cfg(feature = "ruma-client-api")]
    #[cfg_attr(
        docsrs,
        doc(cfg(any(
            feature = "client-api",
            feature = "client-api-c",
            feature = "client-api-s"
        )))
    )]
    #[doc(inline)]
    pub use ruma_client_api as client;
    #[cfg(feature = "ruma-federation-api")]
    #[cfg_attr(
        docsrs,
        doc(cfg(any(
            feature = "federation-api",
            feature = "federation-api-c",
            feature = "federation-api-s"
        )))
    )]
    #[doc(inline)]
    pub use ruma_federation_api as federation;
    #[cfg(feature = "ruma-identity-service-api")]
    #[cfg_attr(
        docsrs,
        doc(cfg(any(
            feature = "identity-service-api",
            feature = "identity-service-api-c",
            feature = "identity-service-api-s"
        )))
    )]
    #[doc(inline)]
    pub use ruma_identity_service_api as identity_service;
    #[cfg(feature = "ruma-push-gateway-api")]
    #[cfg_attr(
        docsrs,
        doc(cfg(any(
            feature = "push-gateway-api",
            feature = "push-gateway-api-c",
            feature = "push-gateway-api-s"
        )))
    )]
    #[doc(inline)]
    pub use ruma_push_gateway_api as push_gateway;
}

#[doc(no_inline)]
pub use assign::assign;
#[doc(no_inline)]
pub use js_int::{int, uint, Int, UInt};
#[cfg(feature = "client-ext-client-api")]
pub use ruma_client::Client;
pub use ruma_common::{
    authentication, device_id, device_key_id, directory, encryption, event_id, exports, matrix_uri,
    mxc_uri, power_levels, presence, push, receipt, room, room_alias_id, room_id, room_version_id,
    serde::Incoming, server_name, server_signing_key_id, thirdparty, to_device, user_id,
    ClientSecret, DeviceId, DeviceKeyAlgorithm, DeviceKeyId, DeviceSignatures, DeviceSigningKeyId,
    EntitySignatures, EventEncryptionAlgorithm, EventId, IdParseError, KeyId, KeyName, MatrixToUri,
    MatrixUri, MilliSecondsSinceUnixEpoch, MxcUri, OwnedClientSecret, OwnedDeviceId,
    OwnedDeviceKeyId, OwnedDeviceSigningKeyId, OwnedEventId, OwnedKeyId, OwnedKeyName, OwnedMxcUri,
    OwnedRoomAliasId, OwnedRoomId, OwnedRoomName, OwnedRoomOrAliasId, OwnedServerName,
    OwnedServerSigningKeyId, OwnedSessionId, OwnedSigningKeyId, OwnedTransactionId, OwnedUserId,
    PrivOwnedStr, RoomAliasId, RoomId, RoomName, RoomOrAliasId, RoomVersionId,
    SecondsSinceUnixEpoch, ServerName, ServerSignatures, ServerSigningKeyId, SessionId, Signatures,
    SigningKeyAlgorithm, TransactionId, UserId,
};
<commit_msg>ruma: Clean up doc formatting<commit_after>#![doc(html_favicon_url = "https://www.ruma.io/favicon.ico")]
#![doc(html_logo_url = "https://www.ruma.io/images/logo.png")]
//! Types and traits for working with the [Matrix](https://matrix.org) protocol.
//!
//! This crate re-exports things from all of the other ruma crates so you don't
//! have to manually keep all the versions in sync.
//!
//! Which crates are re-exported can be configured through cargo features.
//!
//! > ⚠ Some details might be missing because rustdoc has trouble with re-exports so you may need
//! > to refer to other crates' documentations.
//!
//! # API features
//!
//! Depending on which parts of Matrix are relevant to you, activate the following features:
//!
//! * `appservice-api` -- Application Service API.
//! * `client-api` -- Client-Server API.
//! * `federation-api` -- Server-Server (Federation) API.
//! * `identity-service-api` -- Identity Service API.
//! * `push-gateway-api` -- Push Gateway API.
//!
//! These features have `client`- and `server`-optimized variants that are enabled respectively
//! with the `-c` and `-s` suffixes. For example:
//!   * `client-api-c` -- The Client-Server API optimized for the client side.
//!   * `client-api-s` -- The Client-Server API optimized for the server side.
//!
//! # Compatibility feature
//!
//! * `compat` increases compatibility with other parts of the Matrix ecosystem, at the expense of
//!   deviating from the specification.
//!
//! # Convenience features
//!
//! These features are only useful if you want to use a method that requires it:
//!
//! * `either`
//! * `rand`
//! * `markdown`
//!
//! # Unstable features
//!
//! By using these features, you opt out of all semver guarantees Ruma otherwise provides:
//!
//! * `unstable-exhaustive-types` -- Most types in Ruma are marked as non-exhaustive to avoid
//!   breaking changes when new fields are added in the specification. This feature compiles all
//!   types as exhaustive.
//! * `unstable-mscXXXX`, where `XXXX` is the MSC number -- Upcoming Matrix features that may be
//!   subject to change or removal.
//! * `unstable-pre-spec` -- Undocumented Matrix features that may be subject to change or removal.
//!
//! # Common features
//!
//! These submodules are usually activated by the API features when needed:
//!
//! * `api`
//! * `events`
//! * `signatures`
//!
//! # `ruma-client` features
//!
//! The `client` feature activates [`ruma::client`][client], and `client-ext-client-api` activates
//! `ruma-client`s `client-api` feature. All other `client-*` features activate the same feature
//! without the `client-` prefix on `ruma-client`. See the crate's documentation for the effect of
//! these features.
//!
//! If you are viewing this on `docs.rs`, you can have a look at the feature dependencies by
//! clicking **Feature flags** in the toolbar at the top.

#![warn(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]

#[doc(inline)]
pub use ruma_common::serde;

#[cfg(feature = "client")]
#[doc(inline)]
pub use ruma_client as client;
#[cfg(feature = "events")]
#[doc(inline)]
pub use ruma_common::events;
#[cfg(feature = "signatures")]
#[doc(inline)]
pub use ruma_signatures as signatures;
#[cfg(feature = "state-res")]
#[doc(inline)]
pub use ruma_state_res as state_res;

/// (De)serializable types for various [Matrix APIs][apis] requests and responses and abstractions
/// for them.
///
/// [apis]: https://spec.matrix.org/v1.2/#matrix-apis
#[cfg(feature = "api")]
pub mod api {
    pub use ruma_common::api::*;

    #[cfg(feature = "ruma-appservice-api")]
    #[cfg_attr(
        docsrs,
        doc(cfg(any(
            feature = "appservice-api",
            feature = "appservice-api-c",
            feature = "appservice-api-s"
        )))
    )]
    #[doc(inline)]
    pub use ruma_appservice_api as appservice;
    #[cfg(feature = "ruma-client-api")]
    #[cfg_attr(
        docsrs,
        doc(cfg(any(
            feature = "client-api",
            feature = "client-api-c",
            feature = "client-api-s"
        )))
    )]
    #[doc(inline)]
    pub use ruma_client_api as client;
    #[cfg(feature = "ruma-federation-api")]
    #[cfg_attr(
        docsrs,
        doc(cfg(any(
            feature = "federation-api",
            feature = "federation-api-c",
            feature = "federation-api-s"
        )))
    )]
    #[doc(inline)]
    pub use ruma_federation_api as federation;
    #[cfg(feature = "ruma-identity-service-api")]
    #[cfg_attr(
        docsrs,
        doc(cfg(any(
            feature = "identity-service-api",
            feature = "identity-service-api-c",
            feature = "identity-service-api-s"
        )))
    )]
    #[doc(inline)]
    pub use ruma_identity_service_api as identity_service;
    #[cfg(feature = "ruma-push-gateway-api")]
    #[cfg_attr(
        docsrs,
        doc(cfg(any(
            feature = "push-gateway-api",
            feature = "push-gateway-api-c",
            feature = "push-gateway-api-s"
        )))
    )]
    #[doc(inline)]
    pub use ruma_push_gateway_api as push_gateway;
}

#[doc(no_inline)]
pub use assign::assign;
#[doc(no_inline)]
pub use js_int::{int, uint, Int, UInt};
#[cfg(feature = "client-ext-client-api")]
pub use ruma_client::Client;
pub use ruma_common::{
    authentication, device_id, device_key_id, directory, encryption, event_id, exports, matrix_uri,
    mxc_uri, power_levels, presence, push, receipt, room, room_alias_id, room_id, room_version_id,
    serde::Incoming, server_name, server_signing_key_id, thirdparty, to_device, user_id,
    ClientSecret, DeviceId, DeviceKeyAlgorithm, DeviceKeyId, DeviceSignatures, DeviceSigningKeyId,
    EntitySignatures, EventEncryptionAlgorithm, EventId, IdParseError, KeyId, KeyName, MatrixToUri,
    MatrixUri, MilliSecondsSinceUnixEpoch, MxcUri, OwnedClientSecret, OwnedDeviceId,
    OwnedDeviceKeyId, OwnedDeviceSigningKeyId, OwnedEventId, OwnedKeyId, OwnedKeyName, OwnedMxcUri,
    OwnedRoomAliasId, OwnedRoomId, OwnedRoomName, OwnedRoomOrAliasId, OwnedServerName,
    OwnedServerSigningKeyId, OwnedSessionId, OwnedSigningKeyId, OwnedTransactionId, OwnedUserId,
    PrivOwnedStr, RoomAliasId, RoomId, RoomName, RoomOrAliasId, RoomVersionId,
    SecondsSinceUnixEpoch, ServerName, ServerSignatures, ServerSigningKeyId, SessionId, Signatures,
    SigningKeyAlgorithm, TransactionId, UserId,
};
<|endoftext|>


<commit_before>//! Create textures and build texture atlas.

use std::collections::HashMap;
use std::collections::hash_map::Entry::{ Occupied, Vacant };
use std::fmt::{ Debug, Formatter, Error };
use std::path::{ Path, PathBuf };
use std::mem;

use gfx;
use image::{
    self,
    ImageBuffer,
    RgbaImage,
    DynamicImage,
    ImageResult,
    SubImage,
    ImageError,
    GenericImage,
    Pixel
};

pub use gfx_texture::Texture;
pub use gfx_texture::ImageSize;
pub use gfx_texture::Settings as TextureSettings;

// Loads RGBA image from path.
fn load_rgba8(path: &Path) -> ImageResult<RgbaImage> {
    image::open(path).map(|img| match img {
        DynamicImage::ImageRgba8(img) => img,
        img => img.to_rgba()
    })
}

/// An enumeration of ColorMap errors.
pub enum ColorMapError {
    /// The image opening error.
    Img(ImageError),

    /// The image size error.
    Size(u32, u32, String)
}

impl Debug for ColorMapError {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        match *self {
            ColorMapError::Img(ref e) => e.fmt(f),
            ColorMapError::Size(w, h, ref path) =>
                format!("ColorMap expected 256x256, found {}x{} in '{}'", w, h, path).fmt(f)
        }
    }
}

impl From<ImageError> for ColorMapError {
    fn from(img_err: ImageError) -> Self {
        ColorMapError::Img(img_err)
    }
}

/// A 256x256 image that stores colors.
pub struct ColorMap(RgbaImage);

impl ColorMap {
    /// Creates a new `ColorMap` from path.
    pub fn from_path<P>(path: P) -> Result<Self, ColorMapError>
        where P: AsRef<Path>
    {
        let img = try!(load_rgba8(path.as_ref()));

        match img.dimensions() {
            (256, 256) => Ok(ColorMap(img)),
            (w, h) => Err(ColorMapError::Size(w, h, path.as_ref().display().to_string()))
        }
    }

    /// Gets RGB color from the color map.
    pub fn get(&self, x: f32, y: f32) -> [u8; 3] {
        // Clamp to [0.0, 1.0].
        let x = x.max(0.0).min(1.0);
        let y = y.max(0.0).min(1.0);

        // Scale y from [0.0, 1.0] to [0.0, x], forming a triangle.
        let y = x * y;

        // Origin is in the bottom-right corner.
        let x = ((1.0 - x) * 255.0) as u8;
        let y = ((1.0 - y) * 255.0) as u8;

        let col = self.0.get_pixel(x as u32, y as u32).channels();
        [col[0], col[1], col[2]]
    }
}

/// Builds an atlas of textures.
pub struct AtlasBuilder {
    image: RgbaImage,
    // Base path for loading tiles.
    path: PathBuf,
    // Size of an individual tile.
    unit_width: u32,
    unit_height: u32,
    // Size of the entirely occupied square, in tiles.
    completed_tiles_size: u32,
    // Position in the current strip.
    position: u32,
    // Position cache for loaded tiles (in pixels).
    tile_positions: HashMap<String, (u32, u32)>,
    // Lowest-alpha cache for rectangles in the atlas.
    min_alpha_cache: HashMap<(u32, u32, u32, u32), u8>
}

impl AtlasBuilder {
    /// Creates a new `AtlasBuilder`.
    pub fn new<P>(path: P, unit_width: u32, unit_height: u32) -> Self
        where P: Into<PathBuf>
    {
        AtlasBuilder {
            image: ImageBuffer::new(unit_width * 4, unit_height * 4),
            path: path.into(),
            unit_width: unit_width,
            unit_height: unit_height,
            completed_tiles_size: 0,
            position: 0,
            tile_positions: HashMap::new(),
            min_alpha_cache: HashMap::new()
        }
    }

    /// Loads a file into the texture atlas.
    /// Checks if the file is loaded and returns position within the atlas.
    /// The name should be specified without file extension.
    /// PNG is the only supported format.
    pub fn load(&mut self, name: &str) -> (u32, u32) {
        if let Some(&pos) = self.tile_positions.get(name) {
            return pos
        }

        let mut path = self.path.join(name);
        path.set_extension("png");
        let img = load_rgba8(&path).unwrap();

        let (iw, ih) = img.dimensions();
        assert!(iw == self.unit_width);
        assert!((ih % self.unit_height) == 0);
        if ih > self.unit_height {
            println!("ignoring {} extra frames in '{}'", (ih / self.unit_height) - 1, name);
        }

        let (uw, uh) = (self.unit_width, self.unit_height);
        let (w, h) = self.image.dimensions();
        let size = self.completed_tiles_size;

        // Expand the image buffer if necessary.
        if self.position == 0 && (uw * size >= w || uh * size >= h) {
            let old = mem::replace(&mut self.image, ImageBuffer::new(w * 2, h * 2));
            for ix in 0 .. w {
                for iy in 0 .. h {
                    *self.image.get_pixel_mut(ix, iy) = old[(ix, iy)];
                }
            }

            /*
            let mut dest = SubImage::new(&mut self.image, 0, 0, w, h);
            for ((_, _, a), b) in dest.pixels_mut().zip(old.pixels()) {
                *a = *b;
            }
            */
        }

        let (x, y) = if self.position < size {
            (self.position, size)
        } else {
            (size, self.position - size)
        };

        self.position += 1;
        if self.position >= size * 2 + 1 {
            self.position = 0;
            self.completed_tiles_size += 1;
        }

        {
            let (x, y, w, h) = (x * uw, y * uh, uw, uh);
            for ix in 0 .. w {
                for iy in 0 .. h {
                    *self.image.get_pixel_mut(ix + x, iy + y) = img[(ix, iy)];
                }
            }
        }

        /*
        let mut dest = SubImage::new(&mut self.image, x * uw, y * uh, uw, uh);
        for ((_, _, a), b) in dest.pixels_mut().zip(img.pixels()) {
            *a = *b;
        }
        */

        *match self.tile_positions.entry(name.to_string()) {
            Occupied(entry) => entry.into_mut(),
            Vacant(entry) => entry.insert((x * uw, y * uh))
        }
    }

    /// Finds the minimum alpha value in a given sub texture of the image.
    pub fn min_alpha(&mut self, rect: [u32; 4]) -> u8 {
        let x = rect[0];
        let y = rect[1];
        let w = rect[2];
        let h = rect[3];
        if let Some(&alpha) = self.min_alpha_cache.get(&(x, y, w, h)) {
            return alpha
        }

        let tile = SubImage::new(&mut self.image, x, y, w, h);
        let min_alpha = tile.pixels().map(|(_, _, p)| p[3]).min().unwrap_or(0);
        self.min_alpha_cache.insert((x, y, w, h), min_alpha);
        min_alpha
    }

    /// Returns the complete texture atlas as a texture.
    pub fn complete<R, D>(self, device: &mut D) -> Texture<R>
        where R: gfx::Resources, D: gfx::Factory<R>
    {
        Texture::from_image(device, &self.image, false, true)
    }
}
<commit_msg>Upgrade to latest gfx<commit_after>//! Create textures and build texture atlas.

use std::collections::HashMap;
use std::collections::hash_map::Entry::{ Occupied, Vacant };
use std::fmt::{ Debug, Formatter, Error };
use std::path::{ Path, PathBuf };
use std::mem;

use gfx;
use image::{
    self,
    ImageBuffer,
    RgbaImage,
    DynamicImage,
    ImageResult,
    SubImage,
    ImageError,
    GenericImage,
    Pixel
};

pub use gfx_texture::{ Texture, ImageSize, Settings };

// Loads RGBA image from path.
fn load_rgba8(path: &Path) -> ImageResult<RgbaImage> {
    image::open(path).map(|img| match img {
        DynamicImage::ImageRgba8(img) => img,
        img => img.to_rgba()
    })
}

/// An enumeration of ColorMap errors.
pub enum ColorMapError {
    /// The image opening error.
    Img(ImageError),

    /// The image size error.
    Size(u32, u32, String)
}

impl Debug for ColorMapError {
    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
        match *self {
            ColorMapError::Img(ref e) => e.fmt(f),
            ColorMapError::Size(w, h, ref path) =>
                format!("ColorMap expected 256x256, found {}x{} in '{}'", w, h, path).fmt(f)
        }
    }
}

impl From<ImageError> for ColorMapError {
    fn from(img_err: ImageError) -> Self {
        ColorMapError::Img(img_err)
    }
}

/// A 256x256 image that stores colors.
pub struct ColorMap(RgbaImage);

impl ColorMap {
    /// Creates a new `ColorMap` from path.
    pub fn from_path<P>(path: P) -> Result<Self, ColorMapError>
        where P: AsRef<Path>
    {
        let img = try!(load_rgba8(path.as_ref()));

        match img.dimensions() {
            (256, 256) => Ok(ColorMap(img)),
            (w, h) => Err(ColorMapError::Size(w, h, path.as_ref().display().to_string()))
        }
    }

    /// Gets RGB color from the color map.
    pub fn get(&self, x: f32, y: f32) -> [u8; 3] {
        // Clamp to [0.0, 1.0].
        let x = x.max(0.0).min(1.0);
        let y = y.max(0.0).min(1.0);

        // Scale y from [0.0, 1.0] to [0.0, x], forming a triangle.
        let y = x * y;

        // Origin is in the bottom-right corner.
        let x = ((1.0 - x) * 255.0) as u8;
        let y = ((1.0 - y) * 255.0) as u8;

        let col = self.0.get_pixel(x as u32, y as u32).channels();
        [col[0], col[1], col[2]]
    }
}

/// Builds an atlas of textures.
pub struct AtlasBuilder {
    image: RgbaImage,
    // Base path for loading tiles.
    path: PathBuf,
    // Size of an individual tile.
    unit_width: u32,
    unit_height: u32,
    // Size of the entirely occupied square, in tiles.
    completed_tiles_size: u32,
    // Position in the current strip.
    position: u32,
    // Position cache for loaded tiles (in pixels).
    tile_positions: HashMap<String, (u32, u32)>,
    // Lowest-alpha cache for rectangles in the atlas.
    min_alpha_cache: HashMap<(u32, u32, u32, u32), u8>
}

impl AtlasBuilder {
    /// Creates a new `AtlasBuilder`.
    pub fn new<P>(path: P, unit_width: u32, unit_height: u32) -> Self
        where P: Into<PathBuf>
    {
        AtlasBuilder {
            image: ImageBuffer::new(unit_width * 4, unit_height * 4),
            path: path.into(),
            unit_width: unit_width,
            unit_height: unit_height,
            completed_tiles_size: 0,
            position: 0,
            tile_positions: HashMap::new(),
            min_alpha_cache: HashMap::new()
        }
    }

    /// Loads a file into the texture atlas.
    /// Checks if the file is loaded and returns position within the atlas.
    /// The name should be specified without file extension.
    /// PNG is the only supported format.
    pub fn load(&mut self, name: &str) -> (u32, u32) {
        if let Some(&pos) = self.tile_positions.get(name) {
            return pos
        }

        let mut path = self.path.join(name);
        path.set_extension("png");
        let img = load_rgba8(&path).unwrap();

        let (iw, ih) = img.dimensions();
        assert!(iw == self.unit_width);
        assert!((ih % self.unit_height) == 0);
        if ih > self.unit_height {
            println!("ignoring {} extra frames in '{}'", (ih / self.unit_height) - 1, name);
        }

        let (uw, uh) = (self.unit_width, self.unit_height);
        let (w, h) = self.image.dimensions();
        let size = self.completed_tiles_size;

        // Expand the image buffer if necessary.
        if self.position == 0 && (uw * size >= w || uh * size >= h) {
            let old = mem::replace(&mut self.image, ImageBuffer::new(w * 2, h * 2));
            for ix in 0 .. w {
                for iy in 0 .. h {
                    *self.image.get_pixel_mut(ix, iy) = old[(ix, iy)];
                }
            }

            /*
            let mut dest = SubImage::new(&mut self.image, 0, 0, w, h);
            for ((_, _, a), b) in dest.pixels_mut().zip(old.pixels()) {
                *a = *b;
            }
            */
        }

        let (x, y) = if self.position < size {
            (self.position, size)
        } else {
            (size, self.position - size)
        };

        self.position += 1;
        if self.position >= size * 2 + 1 {
            self.position = 0;
            self.completed_tiles_size += 1;
        }

        {
            let (x, y, w, h) = (x * uw, y * uh, uw, uh);
            for ix in 0 .. w {
                for iy in 0 .. h {
                    *self.image.get_pixel_mut(ix + x, iy + y) = img[(ix, iy)];
                }
            }
        }

        /*
        let mut dest = SubImage::new(&mut self.image, x * uw, y * uh, uw, uh);
        for ((_, _, a), b) in dest.pixels_mut().zip(img.pixels()) {
            *a = *b;
        }
        */

        *match self.tile_positions.entry(name.to_string()) {
            Occupied(entry) => entry.into_mut(),
            Vacant(entry) => entry.insert((x * uw, y * uh))
        }
    }

    /// Finds the minimum alpha value in a given sub texture of the image.
    pub fn min_alpha(&mut self, rect: [u32; 4]) -> u8 {
        let x = rect[0];
        let y = rect[1];
        let w = rect[2];
        let h = rect[3];
        if let Some(&alpha) = self.min_alpha_cache.get(&(x, y, w, h)) {
            return alpha
        }

        let tile = SubImage::new(&mut self.image, x, y, w, h);
        let min_alpha = tile.pixels().map(|(_, _, p)| p[3]).min().unwrap_or(0);
        self.min_alpha_cache.insert((x, y, w, h), min_alpha);
        min_alpha
    }

    /// Returns the complete texture atlas as a texture.
    pub fn complete<R, F>(self, factory: &mut F) -> Texture<R>
        where R: gfx::Resources, F: gfx::Factory<R>
    {
        Texture::from_image(factory, &self.image, false, true)
    }
}
<|endoftext|>


<commit_before>use std::ptr;
use std::marker::PhantomData;
use gl;
use gl::types::*;
use context::Context;
use image_data::{Image2d, TextelFormat, ImageFormat};
use types::GLError;

pub struct Texture<T: TextureType> {
    gl_id: GLuint,
    phantom: PhantomData<*mut T>
}
pub type Texture2d = Texture<Tx2d>;
pub type TextureCubeMap = Texture<TxCubeMap>;

impl<T: TextureType> Texture<T> {
    pub fn gl_id(&self) -> GLuint {
        self.gl_id
    }
}

impl<T: TextureType> Drop for Texture<T> {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteTextures(1, &self.gl_id as *const GLuint);
        }
    }
}

impl Context {
    pub unsafe fn gen_texture<T: TextureType>(&self) -> Texture<T> {
        let mut id : GLuint =  0;

        gl::GenTextures(1, &mut id as *mut GLuint);
        dbg_gl_sanity_check! {
            GLError::InvalidValue => "`n` is negative",
            _ => "Unknown error"
        }

        Texture {
            gl_id: id,
            phantom: PhantomData
        }
    }
}

pub trait ImageTargetType {
    fn gl_enum(&self) -> GLenum;
}

pub trait TextureType {
    type ImageTargetType: ImageTargetType;

    fn target() -> TextureBindingTarget;
}

pub struct Tx2d;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Tx2dImageTarget {
    Texture2d = gl::TEXTURE_2D as isize
}

impl ImageTargetType for Tx2dImageTarget {
    fn gl_enum(&self) -> GLenum {
        *self as GLenum
    }
}

impl TextureType for Tx2d {
    type ImageTargetType = Tx2dImageTarget;

    fn target() -> TextureBindingTarget {
        TextureBindingTarget::Texture2d
    }
}

pub struct TxCubeMap;

gl_enum! {
    pub gl_enum TxCubeMapImageTarget {
        CubeMapPositiveX as TEXTURE_CUBE_MAP_POSITIVE_X =
            gl::TEXTURE_CUBE_MAP_POSITIVE_X,
        CubeMapNegativeX as TEXTURE_CUBE_MAP_NEGATIVE_X =
            gl::TEXTURE_CUBE_MAP_NEGATIVE_X,
        CubeMapPositiveY as TEXTURE_CUBE_MAP_POSITIVE_Y =
            gl::TEXTURE_CUBE_MAP_POSITIVE_Y,
        CubeMapNegativeY as TEXTURE_CUBE_MAP_NEGATIVE_Y =
            gl::TEXTURE_CUBE_MAP_NEGATIVE_Y,
        CubeMapPositiveZ as TEXTURE_CUBE_MAP_POSITIVE_Z =
            gl::TEXTURE_CUBE_MAP_POSITIVE_Z,
        CubeMapNegativeZ as TEXTURE_CUBE_MAP_NEGATIVE_Z =
            gl::TEXTURE_CUBE_MAP_NEGATIVE_Z
    }
}

impl ImageTargetType for TxCubeMapImageTarget {
    fn gl_enum(&self) -> GLenum {
        self.gl_enum()
    }
}

impl TextureType for TxCubeMap {
    type ImageTargetType = TxCubeMapImageTarget;

    fn target() -> TextureBindingTarget {
        TextureBindingTarget::TextureCubeMap
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TextureBindingTarget {
    Texture2d = gl::TEXTURE_2D as isize,
    TextureCubeMap = gl::TEXTURE_CUBE_MAP as isize
}

impl TextureBindingTarget {
    pub fn gl_enum(&self) -> GLenum {
        *self as GLenum
    }
}

// TODO: Use type refinements someday...
#[derive(Debug, Clone, Copy)]
pub enum TextureFilter {
    Nearest,
    Linear
}

#[derive(Debug, Clone, Copy)]
pub enum TextureMipmapFilter {
    Filter(TextureFilter),
    MipmapFilter { criterion: TextureFilter, mipmap: TextureFilter }
}

pub const NEAREST : TextureFilter = TextureFilter::Nearest;
pub const LINEAR : TextureFilter = TextureFilter::Linear;
pub const NEAREST_MIPMAP_NEAREST : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Nearest,
        mipmap: TextureFilter::Nearest
    };
pub const LINEAR_MIPMAP_NEAREST : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Linear,
        mipmap: TextureFilter::Nearest
    };
pub const NEAREST_MIPMAP_LINEAR : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Nearest,
        mipmap: TextureFilter::Linear
    };
pub const LINEAR_MIPMAP_LINEAR : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Linear,
        mipmap: TextureFilter::Linear
    };

#[allow(dead_code)]
impl TextureFilter {
    fn from_gl(gl_enum: GLenum) -> Result<Self, ()> {
        match gl_enum {
            gl::NEAREST => { Ok(self::NEAREST) },
            gl::LINEAR => { Ok(self::LINEAR) },
            _ => { Err(()) }
        }
    }

    fn gl_enum(&self) -> GLenum {
        match *self {
            self::NEAREST => gl::NEAREST,
            self::LINEAR => gl::LINEAR
        }
    }
}

#[allow(dead_code)]
impl TextureMipmapFilter {
    fn from_gl(gl_enum: GLenum) -> Result<Self, ()> {
        match gl_enum {
            gl::NEAREST => { Ok(TextureMipmapFilter::Filter(self::NEAREST)) },
            gl::LINEAR => { Ok(TextureMipmapFilter::Filter(self::LINEAR)) },
            gl::NEAREST_MIPMAP_NEAREST => { Ok(self::NEAREST_MIPMAP_NEAREST) },
            gl::LINEAR_MIPMAP_NEAREST => { Ok(self::LINEAR_MIPMAP_NEAREST) },
            gl::NEAREST_MIPMAP_LINEAR => { Ok(self::NEAREST_MIPMAP_LINEAR) },
            gl::LINEAR_MIPMAP_LINEAR => { Ok(self::LINEAR_MIPMAP_LINEAR) },
            _ => { Err(()) }
        }
    }

    fn gl_enum(&self) -> GLenum {
        match *self {
            TextureMipmapFilter::Filter(self::LINEAR) => { gl::LINEAR },
            TextureMipmapFilter::Filter(self::NEAREST) => { gl::NEAREST },
            self::NEAREST_MIPMAP_NEAREST => { gl::NEAREST_MIPMAP_NEAREST },
            self::LINEAR_MIPMAP_NEAREST => { gl::LINEAR_MIPMAP_NEAREST },
            self::NEAREST_MIPMAP_LINEAR => { gl::NEAREST_MIPMAP_LINEAR },
            self::LINEAR_MIPMAP_LINEAR => { gl::LINEAR_MIPMAP_LINEAR }
        }
    }
}

impl From<TextureFilter> for TextureMipmapFilter {
    fn from(filter: TextureFilter) -> TextureMipmapFilter {
        TextureMipmapFilter::Filter(filter)
    }
}

gl_enum! {
    pub gl_enum TextureWrapMode {
        ClampToEdge as CLAMP_TO_EDGE = gl::CLAMP_TO_EDGE,
        MirroredRepeat as MIRRORED_REPEAT = gl::MIRRORED_REPEAT,
        Repeat as REPEAT = gl::REPEAT
    }
}

unsafe fn _tex_parameter_iv(target: TextureBindingTarget,
                            pname: GLenum,
                            params: *const GLint)
{
    gl::TexParameteriv(target.gl_enum(), pname, params);
    dbg_gl_sanity_check! {
        GLError::InvalidEnum => "`target` or `pname` is not an accepted defined value, or `params` should have defined a symbolic constant and does not",
        _ => "Unknown error"
    }
}

unsafe fn _tex_image_2d<T: ImageTargetType>(target: T,
                                            level: u32,
                                            internal_format: TextelFormat,
                                            width: u32,
                                            height: u32,
                                            border: u32,
                                            format: ImageFormat,
                                            image_ptr: *const u8) {
    debug_assert!(internal_format == format.textel_format);
    gl::TexImage2D(target.gl_enum(),
                   level as GLint,
                   internal_format.gl_enum() as GLint,
                   width as GLint,
                   height as GLint,
                   border as GLint,
                   format.textel_format.gl_enum(),
                   format.textel_type.gl_enum(),
                   image_ptr as *const GLvoid);
    dbg_gl_sanity_check! {
        GLError::InvalidEnum => "`target`, `format`, or `type` is not an accepted value",
        GLError::InvalidValue => "`target`, `level`, `internalformat`, `width`, `height`, or `border` is an invalid value",
        GLError::InvalidOperation => "`format` conflicts with either `internalformat` or `type`",
        _ => "Unknown error"
    }
}

pub trait TextureBinding {
    type TextureType: TextureType;

    fn target() -> TextureBindingTarget {
        Self::TextureType::target()
    }

    fn set_min_filter<F: Into<TextureMipmapFilter>>(&mut self, filter: F) {
        let gl_int = filter.into().gl_enum() as GLint;
        unsafe {
            _tex_parameter_iv(Self::target(),
                              gl::TEXTURE_MIN_FILTER,
                              &gl_int as *const GLint);
        }
    }

    fn set_mag_filter(&mut self, filter: TextureFilter) {
        let gl_int = filter.gl_enum() as GLint;
        unsafe {
            _tex_parameter_iv(Self::target(),
                              gl::TEXTURE_MAG_FILTER,
                              &gl_int as *const GLint);
        }
    }

    fn set_wrap_s(&mut self, wrap_mode: TextureWrapMode) {
        let gl_int = wrap_mode.gl_enum() as GLint;
        unsafe {
            _tex_parameter_iv(Self::target(),
                              gl::TEXTURE_WRAP_S,
                              &gl_int as *const GLint);
        }
    }

    fn set_wrap_t(&mut self, wrap_mode: TextureWrapMode) {
        let gl_int = wrap_mode.gl_enum() as GLint;
        unsafe {
            _tex_parameter_iv(Self::target(),
                              gl::TEXTURE_WRAP_T,
                              &gl_int as *const GLint);
        }
    }

    fn image_2d<I>(&mut self,
                   target: <Self::TextureType as TextureType>::ImageTargetType,
                   level: u32,
                   img: &I)
        where I: Image2d
    {
        unsafe {
            _tex_image_2d(target,
                          level,
                          img.format().textel_format,
                          img.width() as u32,
                          img.height() as u32,
                          0,
                          img.format(),
                          img.textel_bytes().as_ptr());
        }
    }

    fn image_2d_empty<T: ImageTargetType>(&mut self,
                                          target: T,
                                          level: u32,
                                          format: ImageFormat,
                                          width: u32,
                                          height: u32)
    {
        unsafe {
            _tex_image_2d(target,
                          level,
                          format.textel_format,
                          width,
                          height,
                          0,
                          format,
                          ptr::null());
        }
    }
}

pub struct Texture2dBinding<'a> {
    phantom: PhantomData<&'a mut Texture2d>
}

impl<'a> TextureBinding for Texture2dBinding<'a> {
    type TextureType = Tx2d;
}

pub struct TextureCubeMapBinding<'a> {
    phantom: PhantomData<&'a mut TextureCubeMap>
}

impl<'a> TextureBinding for TextureCubeMapBinding<'a> {
    type TextureType = TxCubeMap;
}

unsafe fn _bind_texture<T: TextureType>(texture: &mut Texture<T>) {
    gl::BindTexture(T::target().gl_enum(), texture.gl_id());
    dbg_gl_error! {
        GLError::InvalidEnum => "`target` is not one of the allowed values",
        GLError::InvalidOperation => "`texture` was created with a target that doesn't match `target`",
        _ => "Unknown error"
    }
}

pub struct Texture2dBinder;
impl Texture2dBinder {
    pub fn bind(&mut self, texture: &mut Texture2d) -> Texture2dBinding {
        unsafe {
            _bind_texture(texture);
        }
        Texture2dBinding { phantom: PhantomData }
    }
}

pub struct TextureCubeMapBinder;
impl TextureCubeMapBinder {
    pub fn bind(&mut self, texture: &mut TextureCubeMap)
        -> TextureCubeMapBinding
    {
        unsafe {
            _bind_texture(texture);
        }
        TextureCubeMapBinding { phantom: PhantomData }
    }
}
<commit_msg>Add `VariantTexture2d` unit struct<commit_after>use std::ptr;
use std::marker::PhantomData;
use gl;
use gl::types::*;
use context::Context;
use image_data::{Image2d, TextelFormat, ImageFormat};
use types::GLError;

pub struct Texture<T: TextureType> {
    gl_id: GLuint,
    phantom: PhantomData<*mut T>
}
pub type Texture2d = Texture<Tx2d>;
pub type TextureCubeMap = Texture<TxCubeMap>;

impl<T: TextureType> Texture<T> {
    pub fn gl_id(&self) -> GLuint {
        self.gl_id
    }
}

impl<T: TextureType> Drop for Texture<T> {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteTextures(1, &self.gl_id as *const GLuint);
        }
    }
}

impl Context {
    pub unsafe fn gen_texture<T: TextureType>(&self) -> Texture<T> {
        let mut id : GLuint =  0;

        gl::GenTextures(1, &mut id as *mut GLuint);
        dbg_gl_sanity_check! {
            GLError::InvalidValue => "`n` is negative",
            _ => "Unknown error"
        }

        Texture {
            gl_id: id,
            phantom: PhantomData
        }
    }
}

pub trait ImageTargetType {
    fn gl_enum(&self) -> GLenum;
}

pub trait TextureType {
    type ImageTargetType: ImageTargetType;

    fn target() -> TextureBindingTarget;
}

pub struct Tx2d;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Tx2dImageTarget {
    Texture2d = gl::TEXTURE_2D as isize
}

impl ImageTargetType for Tx2dImageTarget {
    fn gl_enum(&self) -> GLenum {
        *self as GLenum
    }
}

impl TextureType for Tx2d {
    type ImageTargetType = Tx2dImageTarget;

    fn target() -> TextureBindingTarget {
        TextureBindingTarget::Texture2d
    }
}

pub struct TxCubeMap;

gl_enum! {
    pub gl_enum TxCubeMapImageTarget {
        CubeMapPositiveX as TEXTURE_CUBE_MAP_POSITIVE_X =
            gl::TEXTURE_CUBE_MAP_POSITIVE_X,
        CubeMapNegativeX as TEXTURE_CUBE_MAP_NEGATIVE_X =
            gl::TEXTURE_CUBE_MAP_NEGATIVE_X,
        CubeMapPositiveY as TEXTURE_CUBE_MAP_POSITIVE_Y =
            gl::TEXTURE_CUBE_MAP_POSITIVE_Y,
        CubeMapNegativeY as TEXTURE_CUBE_MAP_NEGATIVE_Y =
            gl::TEXTURE_CUBE_MAP_NEGATIVE_Y,
        CubeMapPositiveZ as TEXTURE_CUBE_MAP_POSITIVE_Z =
            gl::TEXTURE_CUBE_MAP_POSITIVE_Z,
        CubeMapNegativeZ as TEXTURE_CUBE_MAP_NEGATIVE_Z =
            gl::TEXTURE_CUBE_MAP_NEGATIVE_Z
    }
}

impl ImageTargetType for TxCubeMapImageTarget {
    fn gl_enum(&self) -> GLenum {
        self.gl_enum()
    }
}

impl TextureType for TxCubeMap {
    type ImageTargetType = TxCubeMapImageTarget;

    fn target() -> TextureBindingTarget {
        TextureBindingTarget::TextureCubeMap
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TextureBindingTarget {
    Texture2d = gl::TEXTURE_2D as isize,
    TextureCubeMap = gl::TEXTURE_CUBE_MAP as isize
}

impl TextureBindingTarget {
    pub fn gl_enum(&self) -> GLenum {
        *self as GLenum
    }
}

// HACK: Done to allow glitter::TEXTURE_2D as both a
//       TextureBindingTarget and Tx2dImageTarget
pub struct VariantTexture2d;

impl From<VariantTexture2d> for TextureBindingTarget {
    fn from(_: VariantTexture2d) -> TextureBindingTarget {
        TextureBindingTarget::Texture2d
    }
}

impl From<VariantTexture2d> for Tx2dImageTarget {
    fn from(_: VariantTexture2d) -> Tx2dImageTarget {
        Tx2dImageTarget::Texture2d
    }
}

impl ImageTargetType for VariantTexture2d {
    fn gl_enum(&self) -> GLenum {
        gl::TEXTURE_2D
    }
}

// TODO: Use type refinements someday...
#[derive(Debug, Clone, Copy)]
pub enum TextureFilter {
    Nearest,
    Linear
}

#[derive(Debug, Clone, Copy)]
pub enum TextureMipmapFilter {
    Filter(TextureFilter),
    MipmapFilter { criterion: TextureFilter, mipmap: TextureFilter }
}

pub const NEAREST : TextureFilter = TextureFilter::Nearest;
pub const LINEAR : TextureFilter = TextureFilter::Linear;
pub const NEAREST_MIPMAP_NEAREST : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Nearest,
        mipmap: TextureFilter::Nearest
    };
pub const LINEAR_MIPMAP_NEAREST : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Linear,
        mipmap: TextureFilter::Nearest
    };
pub const NEAREST_MIPMAP_LINEAR : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Nearest,
        mipmap: TextureFilter::Linear
    };
pub const LINEAR_MIPMAP_LINEAR : TextureMipmapFilter =
    TextureMipmapFilter::MipmapFilter {
        criterion: TextureFilter::Linear,
        mipmap: TextureFilter::Linear
    };

#[allow(dead_code)]
impl TextureFilter {
    fn from_gl(gl_enum: GLenum) -> Result<Self, ()> {
        match gl_enum {
            gl::NEAREST => { Ok(self::NEAREST) },
            gl::LINEAR => { Ok(self::LINEAR) },
            _ => { Err(()) }
        }
    }

    fn gl_enum(&self) -> GLenum {
        match *self {
            self::NEAREST => gl::NEAREST,
            self::LINEAR => gl::LINEAR
        }
    }
}

#[allow(dead_code)]
impl TextureMipmapFilter {
    fn from_gl(gl_enum: GLenum) -> Result<Self, ()> {
        match gl_enum {
            gl::NEAREST => { Ok(TextureMipmapFilter::Filter(self::NEAREST)) },
            gl::LINEAR => { Ok(TextureMipmapFilter::Filter(self::LINEAR)) },
            gl::NEAREST_MIPMAP_NEAREST => { Ok(self::NEAREST_MIPMAP_NEAREST) },
            gl::LINEAR_MIPMAP_NEAREST => { Ok(self::LINEAR_MIPMAP_NEAREST) },
            gl::NEAREST_MIPMAP_LINEAR => { Ok(self::NEAREST_MIPMAP_LINEAR) },
            gl::LINEAR_MIPMAP_LINEAR => { Ok(self::LINEAR_MIPMAP_LINEAR) },
            _ => { Err(()) }
        }
    }

    fn gl_enum(&self) -> GLenum {
        match *self {
            TextureMipmapFilter::Filter(self::LINEAR) => { gl::LINEAR },
            TextureMipmapFilter::Filter(self::NEAREST) => { gl::NEAREST },
            self::NEAREST_MIPMAP_NEAREST => { gl::NEAREST_MIPMAP_NEAREST },
            self::LINEAR_MIPMAP_NEAREST => { gl::LINEAR_MIPMAP_NEAREST },
            self::NEAREST_MIPMAP_LINEAR => { gl::NEAREST_MIPMAP_LINEAR },
            self::LINEAR_MIPMAP_LINEAR => { gl::LINEAR_MIPMAP_LINEAR }
        }
    }
}

impl From<TextureFilter> for TextureMipmapFilter {
    fn from(filter: TextureFilter) -> TextureMipmapFilter {
        TextureMipmapFilter::Filter(filter)
    }
}

gl_enum! {
    pub gl_enum TextureWrapMode {
        ClampToEdge as CLAMP_TO_EDGE = gl::CLAMP_TO_EDGE,
        MirroredRepeat as MIRRORED_REPEAT = gl::MIRRORED_REPEAT,
        Repeat as REPEAT = gl::REPEAT
    }
}

unsafe fn _tex_parameter_iv(target: TextureBindingTarget,
                            pname: GLenum,
                            params: *const GLint)
{
    gl::TexParameteriv(target.gl_enum(), pname, params);
    dbg_gl_sanity_check! {
        GLError::InvalidEnum => "`target` or `pname` is not an accepted defined value, or `params` should have defined a symbolic constant and does not",
        _ => "Unknown error"
    }
}

unsafe fn _tex_image_2d<T: ImageTargetType>(target: T,
                                            level: u32,
                                            internal_format: TextelFormat,
                                            width: u32,
                                            height: u32,
                                            border: u32,
                                            format: ImageFormat,
                                            image_ptr: *const u8) {
    debug_assert!(internal_format == format.textel_format);
    gl::TexImage2D(target.gl_enum(),
                   level as GLint,
                   internal_format.gl_enum() as GLint,
                   width as GLint,
                   height as GLint,
                   border as GLint,
                   format.textel_format.gl_enum(),
                   format.textel_type.gl_enum(),
                   image_ptr as *const GLvoid);
    dbg_gl_sanity_check! {
        GLError::InvalidEnum => "`target`, `format`, or `type` is not an accepted value",
        GLError::InvalidValue => "`target`, `level`, `internalformat`, `width`, `height`, or `border` is an invalid value",
        GLError::InvalidOperation => "`format` conflicts with either `internalformat` or `type`",
        _ => "Unknown error"
    }
}

pub trait TextureBinding {
    type TextureType: TextureType;

    fn target() -> TextureBindingTarget {
        Self::TextureType::target()
    }

    fn set_min_filter<F: Into<TextureMipmapFilter>>(&mut self, filter: F) {
        let gl_int = filter.into().gl_enum() as GLint;
        unsafe {
            _tex_parameter_iv(Self::target(),
                              gl::TEXTURE_MIN_FILTER,
                              &gl_int as *const GLint);
        }
    }

    fn set_mag_filter(&mut self, filter: TextureFilter) {
        let gl_int = filter.gl_enum() as GLint;
        unsafe {
            _tex_parameter_iv(Self::target(),
                              gl::TEXTURE_MAG_FILTER,
                              &gl_int as *const GLint);
        }
    }

    fn set_wrap_s(&mut self, wrap_mode: TextureWrapMode) {
        let gl_int = wrap_mode.gl_enum() as GLint;
        unsafe {
            _tex_parameter_iv(Self::target(),
                              gl::TEXTURE_WRAP_S,
                              &gl_int as *const GLint);
        }
    }

    fn set_wrap_t(&mut self, wrap_mode: TextureWrapMode) {
        let gl_int = wrap_mode.gl_enum() as GLint;
        unsafe {
            _tex_parameter_iv(Self::target(),
                              gl::TEXTURE_WRAP_T,
                              &gl_int as *const GLint);
        }
    }

    fn image_2d<I>(&mut self,
                   target: <Self::TextureType as TextureType>::ImageTargetType,
                   level: u32,
                   img: &I)
        where I: Image2d
    {
        unsafe {
            _tex_image_2d(target,
                          level,
                          img.format().textel_format,
                          img.width() as u32,
                          img.height() as u32,
                          0,
                          img.format(),
                          img.textel_bytes().as_ptr());
        }
    }

    fn image_2d_empty<T: ImageTargetType>(&mut self,
                                          target: T,
                                          level: u32,
                                          format: ImageFormat,
                                          width: u32,
                                          height: u32)
    {
        unsafe {
            _tex_image_2d(target,
                          level,
                          format.textel_format,
                          width,
                          height,
                          0,
                          format,
                          ptr::null());
        }
    }
}

pub struct Texture2dBinding<'a> {
    phantom: PhantomData<&'a mut Texture2d>
}

impl<'a> TextureBinding for Texture2dBinding<'a> {
    type TextureType = Tx2d;
}

pub struct TextureCubeMapBinding<'a> {
    phantom: PhantomData<&'a mut TextureCubeMap>
}

impl<'a> TextureBinding for TextureCubeMapBinding<'a> {
    type TextureType = TxCubeMap;
}

unsafe fn _bind_texture<T: TextureType>(texture: &mut Texture<T>) {
    gl::BindTexture(T::target().gl_enum(), texture.gl_id());
    dbg_gl_error! {
        GLError::InvalidEnum => "`target` is not one of the allowed values",
        GLError::InvalidOperation => "`texture` was created with a target that doesn't match `target`",
        _ => "Unknown error"
    }
}

pub struct Texture2dBinder;
impl Texture2dBinder {
    pub fn bind(&mut self, texture: &mut Texture2d) -> Texture2dBinding {
        unsafe {
            _bind_texture(texture);
        }
        Texture2dBinding { phantom: PhantomData }
    }
}

pub struct TextureCubeMapBinder;
impl TextureCubeMapBinder {
    pub fn bind(&mut self, texture: &mut TextureCubeMap)
        -> TextureCubeMapBinding
    {
        unsafe {
            _bind_texture(texture);
        }
        TextureCubeMapBinding { phantom: PhantomData }
    }
}
<|endoftext|>