Skip to content
This repository has been archived by the owner on Oct 4, 2020. It is now read-only.

Don't require wrapping libraw calls in a handler #57

Merged
merged 18 commits into from
Jun 26, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 0 additions & 12 deletions libraw/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +0,0 @@
import libraw.bindings

libraw = libraw.bindings.libraw
"""
A handle to the LibRaw binary installed on the end users machine.

This should always be imported first by anything that wants to use `libraw`:

.. sourcecode:: python

from libraw import libraw
"""
98 changes: 87 additions & 11 deletions libraw/bindings.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,97 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The :class:`libraw.bindings` module handles linking against the LibRaw binary.
It does not provide an API.
"""

from ctypes import * # noqa
from ctypes import util

from libraw import errors
from libraw.errors import c_error
from libraw.structs import libraw_data_t, libraw_processed_image_t

# TODO: This is necessary because Travis CI is still using Ubuntu 12.04
try:
# TODO: This will do bad things if the API version isn't 10
libraw = cdll.LoadLibrary(util.find_library('raw')) # pragma: no cover
libraw.libraw_init.restype = POINTER(libraw_data_t) # pragma: no cover
libraw.libraw_dcraw_make_mem_image.restype = POINTER( # pragme: no cover
libraw_processed_image_t # pragma: no cover
) # pragma: no cover
except: # pragma: no cover
libraw = cdll.LoadLibrary('') # pragma: no cover

class LibRaw(CDLL):

"""
A :class:`ctypes.CDLL` that links against `libraw.so` (or the equivalent on
your platform).
"""

def __init__(self): # pragma: no cover
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think __init__is usually the first method. Not sure if it's an actual convention but I've always seen it like that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

libraw = util.find_library('raw')
try:
if libraw is not None:
super(LibRaw, self).__init__(libraw)
else:
raise ImportError
except (ImportError, AttributeError, OSError, IOError):
raise ImportError('Cannot find LibRaw on your system!')

# Define return types

self.libraw_init.restype = POINTER(libraw_data_t)
self.libraw_version.restype = c_char_p
self.libraw_strprogress.restype = c_char_p
self.libraw_versionNumber.restype = c_int
self.libraw_cameraCount.restype = c_int
self.libraw_cameraList.restype = POINTER(
c_char_p * self.libraw_cameraCount()
)
self.libraw_unpack_function_name.restype = c_char_p
self.libraw_subtract_black.restype = POINTER(libraw_data_t)
self.libraw_open_file.restype = c_error
self.libraw_open_file_ex.restype = c_error
self.libraw_open_buffer.restype = c_error
self.libraw_unpack.restype = c_error
self.libraw_unpack_thumb.restype = c_error
self.libraw_adjust_sizes_info_only.restype = c_error
self.libraw_dcraw_ppm_tiff_writer.restype = c_error
self.libraw_dcraw_thumb_writer.restype = c_error
self.libraw_dcraw_process.restype = c_error
self.libraw_dcraw_make_mem_image.restype = POINTER(
libraw_processed_image_t)
self.libraw_dcraw_make_mem_thumb.restype = POINTER(
libraw_processed_image_t)
self.libraw_raw2image.restype = c_error
self.libraw_get_decoder_info.restype = c_error
self.libraw_COLOR.restype = c_error
try:
self.libraw_open_wfile.restype = c_error
self.libraw_open_wfile_ex.restype = c_error
except AttributeError:
pass

@property
def version_number(self):
"""
A numeric representation of the version of LibRaw which we have linked
against in ``(Major, Minor, Patch)`` form. eg. ::

(0, 16, 1)

:returns: The version number
:rtype: :class:`3 tuple`
"""
v = self.libraw_versionNumber()
return ((v >> 16) & 0x0000ff, (v >> 8) & 0x0000ff, v & 0x0000ff)

@property
def version(self):
"""
A string representation of the version of LibRaw which we have linked
against. eg. ::

"0.16.1-Release"

:returns: The version
:rtype: :class:`basestring`
"""
return self.libraw_version().decode('utf-8')

def __getitem__(self, name):
func = super(LibRaw, self).__getitem__(name)

func.errcheck = errors.check_call

return func
22 changes: 19 additions & 3 deletions libraw/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"""

from ctypes import c_int


class c_error(c_int):

"""
An error type for LibRaw (since LibRaw errors are ints and you can't
distinguish between functions that return an error and functions that
return an int that doesn't code for an error).
"""


class UnspecifiedError(Exception):

Expand Down Expand Up @@ -90,15 +101,18 @@ class BadCrop(Exception):
"""


def check_call(exit_code):
def check_call(exit_code, func, arguments):
"""
Throws a Python error which corresponds to the given LibRaw exit code.

:param exit_code: the exit code returned by a LibRaw function
:type exit_code: :class:`int`
:returns: Returns :param:`exit_code` or throws an error from
:class:`libraw.errors`
:rtype: :class:`type(exit_code)`
"""

if exit_code is not 0:
if func.restype is c_error and exit_code.value != 0:
raise {
-1: UnspecifiedError,
-2: FileUnsupported,
Expand All @@ -112,4 +126,6 @@ def check_call(exit_code):
-100009: IOError,
-100010: CancelledByCallback,
-100011: BadCrop
}[exit_code]
}[exit_code.value]

return exit_code
37 changes: 17 additions & 20 deletions rawkit/raw.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@

import ctypes

from libraw import libraw
from libraw import errors as e


from rawkit.errors import NoFileSpecified, InvalidFileType
from libraw.bindings import LibRaw

from rawkit.metadata import Metadata
from rawkit.options import Options

Expand Down Expand Up @@ -40,10 +38,9 @@ def __init__(self, filename=None):
"""Initializes a new Raw object."""
if filename is None:
raise NoFileSpecified()
self.data = libraw.libraw_init(0)
e.check_call(
libraw.libraw_open_file(self.data, filename.encode('ascii'))
)
self.libraw = LibRaw()
self.data = self.libraw.libraw_init(0)
self.libraw.libraw_open_file(self.data, filename.encode('ascii'))

self.options = Options()

Expand All @@ -60,24 +57,24 @@ def __exit__(self, exc_type, exc_value, traceback):

def close(self):
"""Free the underlying raw representation."""
e.check_call(libraw.libraw_close(self.data))
self.libraw.libraw_close(self.data)

def unpack(self):
"""Unpack the raw data."""
if not self.image_unpacked:
e.check_call(libraw.libraw_unpack(self.data))
self.libraw.libraw_unpack(self.data)
self.image_unpacked = True

def unpack_thumb(self):
"""Unpack the thumbnail data."""
if not self.thumb_unpacked:
e.check_call(libraw.libraw_unpack_thumb(self.data))
self.libraw.libraw_unpack_thumb(self.data)
self.thumb_unpacked = True

def process(self):
"""Process the raw data based on self.options"""
self.options._map_to_libraw_params(self.data.contents.params)
e.check_call(libraw.libraw_dcraw_process(self.data))
self.libraw.libraw_dcraw_process(self.data)

def save(self, filename=None, filetype='ppm'):
"""
Expand All @@ -99,8 +96,8 @@ def save(self, filename=None, filetype='ppm'):
self.unpack()
self.process()

e.check_call(libraw.libraw_dcraw_ppm_tiff_writer(
self.data, filename.encode('ascii')))
self.libraw.libraw_dcraw_ppm_tiff_writer(
self.data, filename.encode('ascii'))

def save_thumb(self, filename=None):
"""
Expand All @@ -111,8 +108,8 @@ def save_thumb(self, filename=None):
"""
self.unpack_thumb()

e.check_call(libraw.libraw_dcraw_thumb_writer(
self.data, filename.encode('ascii')))
self.libraw.libraw_dcraw_thumb_writer(
self.data, filename.encode('ascii'))

def to_buffer(self):
"""
Expand All @@ -124,13 +121,13 @@ def to_buffer(self):
self.unpack()
self.process()

processed_image = libraw.libraw_dcraw_make_mem_image(self.data)
processed_image = self.libraw.libraw_dcraw_make_mem_image(self.data)
data_pointer = ctypes.cast(
processed_image.contents.data,
ctypes.POINTER(ctypes.c_byte * processed_image.contents.data_size)
)
data = bytearray(data_pointer.contents)
e.check_call(libraw.libraw_dcraw_clear_mem(processed_image))
self.libraw.libraw_dcraw_clear_mem(processed_image)

return data

Expand All @@ -143,13 +140,13 @@ def thumbnail_to_buffer(self):
"""
self.unpack_thumb()

processed_image = libraw.libraw_dcraw_make_mem_thumb(self.data)
processed_image = self.libraw.libraw_dcraw_make_mem_thumb(self.data)
data_pointer = ctypes.cast(
processed_image.contents.data,
ctypes.POINTER(ctypes.c_byte * processed_image.contents.data_size)
)
data = bytearray(data_pointer.contents)
e.check_call(libraw.libraw_dcraw_clear_mem(processed_image))
self.libraw.libraw_dcraw_clear_mem(processed_image)

return data

Expand Down
6 changes: 5 additions & 1 deletion rawkit/util.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import ctypes
import os

from libraw import libraw
from libraw.bindings import LibRaw


def discover(path):
Expand All @@ -11,6 +11,7 @@ def discover(path):
:type path: :class:`str`
"""
file_list = []
libraw = LibRaw()
raw = libraw.libraw_init(0)

for root, _, files in os.walk(path):
Expand All @@ -32,6 +33,9 @@ def camera_list():
:returns: A list of supported cameras
:rtype: :class:`str tuple`
"""

libraw = LibRaw()
print(libraw)
libraw.libraw_cameraList.restype = ctypes.POINTER(
ctypes.c_char_p * libraw.libraw_cameraCount()
)
Expand Down