コード例 #1
0
ファイル: exception.py プロジェクト: eevee/sanpera
def magick_raise(exc, force=False):
    """If the given `ExceptionInfo` pointer contains an exception, convert it
    to a Python one and throw it.

    Set `force` to True to throw a generic error even if the pointer doesn't
    seem to indicate an exception.  This is useful in cases where a function
    uses both exceptions and `NULL` returns, which are sadly all too common in
    ImageMagick.
    """
    if exc == ffi.NULL or exc.severity == lib.UndefinedException:
        if force:
            raise MysteryError
        return

    # TODO have more exception classes

    # An exception's description tends to be blank; the actual message
    # is in `reason`
    if exc.reason == ffi.NULL:
        message = b''
    else:
        message = ffi.string(exc.reason)

    severity_name = ffi.string(ffi.cast("ExceptionType", exc.severity))
    # severity_name is an enum name and thus a string, not bytes
    message = message + b'   ' + severity_name.encode('ascii')

    if exc.severity < lib.ErrorException:
        # This is a warning, so do a warning
        cls = magick_exception_map.get(exc.severity, GenericMagickWarning)
        warnings.warn(cls(message))
    else:
        # ERROR ERROR
        cls = magick_exception_map.get(exc.severity, GenericMagickError)
        raise cls(message)
コード例 #2
0
ファイル: exception.py プロジェクト: pombreda/sanpera
def magick_raise(exc, force=False):
    """If the given `ExceptionInfo` pointer contains an exception, convert it
    to a Python one and throw it.

    Set `force` to True to throw a generic error even if the pointer doesn't
    seem to indicate an exception.  This is useful in cases where a function
    uses both exceptions and `NULL` returns, which are sadly all too common in
    ImageMagick.
    """
    if exc == ffi.NULL or exc.severity == lib.UndefinedException:
        if force:
            raise MysteryError
        return

    # TODO have more exception classes

    # An exception's description tends to be blank; the actual message
    # is in `reason`
    if exc.reason == ffi.NULL:
        message = b''
    else:
        message = ffi.string(exc.reason)

    severity_name = ffi.string(ffi.cast("ExceptionType", exc.severity))
    # severity_name is an enum name and thus a string, not bytes
    message = message + b'   ' + severity_name.encode('ascii')

    if exc.severity < lib.ErrorException:
        # This is a warning, so do a warning
        cls = magick_exception_map.get(exc.severity, GenericMagickWarning)
        warnings.warn(cls(message))
    else:
        # ERROR ERROR
        cls = magick_exception_map.get(exc.severity, GenericMagickError)
        raise cls(message)
コード例 #3
0
ファイル: image.py プロジェクト: eevee/sanpera
    def read(cls, filename):
        with open(filename, "rb") as fh:
            image_info = blank_image_info()
            image_info.file = ffi.cast("FILE *", fh)

            with magick_try() as exc:
                ptr = lib.ReadImage(image_info, exc.ptr)
                exc.check(ptr == ffi.NULL)

        return cls(ptr)
コード例 #4
0
ファイル: image.py プロジェクト: pombreda/sanpera
    def read(cls, filename):
        with open(filename, "rb") as fh:
            image_info = blank_image_info()
            image_info.file = ffi.cast("FILE *", fh)

            with magick_try() as exc:
                ptr = lib.ReadImage(image_info, exc.ptr)
                exc.check(ptr == ffi.NULL)

        return cls(ptr)
コード例 #5
0
ファイル: filters.py プロジェクト: pombreda/sanpera
    def __call__(self, *frames, **kwargs):
        channel = kwargs.get('channel', lib.DefaultChannels)
        c_channel = ffi.cast('ChannelType', channel)

        steps = ffi.new("sanpera_evaluate_step[]", self.compiled_steps)
        c_frames = ffi.new("Image *[]", [f._frame for f in frames] + [ffi.NULL])

        with magick_try() as exc:
            new_frame = ffi.gc(
                lib.sanpera_evaluate_filter(c_frames, steps, c_channel, exc.ptr),
                lib.DestroyImageList)

        return Image(new_frame)
コード例 #6
0
ファイル: filters.py プロジェクト: eevee/sanpera
    def __call__(self, *frames, **kwargs):
        channel = kwargs.get('channel', lib.DefaultChannels)
        c_channel = ffi.cast('ChannelType', channel)

        steps = ffi.new("sanpera_evaluate_step[]", self.compiled_steps)
        c_frames = ffi.new("Image *[]", [f._frame for f in frames] + [ffi.NULL])

        with magick_try() as exc:
            # TODO can this raise an exception /but also/ return a new value?
            # is that a thing i should be handling better
            new_frame = lib.sanpera_evaluate_filter(
                c_frames, steps, c_channel, exc.ptr)

        return Image(new_frame)
コード例 #7
0
ファイル: image.py プロジェクト: eevee/sanpera
    def write(self, filename, format=None):
        if not self._frames:
            raise EmptyImageError

        with open(filename, "wb") as fh:
            image_info = blank_image_info()
            image_info.file = ffi.cast("FILE *", fh)

            # Force writing to a single file
            image_info.adjoin = lib.MagickTrue

            if format:
                # If the caller provided an explicit format, pass it along
                # Make sure not to overflow the char[]
                # TODO maybe just error out when this happens
                image_info.magick = format.encode('ascii')[:lib.MaxTextExtent]
            elif self._frames[0]._frame.magick[0] == b'\0':
                # Uhoh; no format provided and nothing given by caller
                raise MissingFormatError
            # TODO detect format from filename if explicitly asked to do so

            with self._link_frames(self._frames) as ptr:
                lib.WriteImage(image_info, ptr)
                magick_raise(ptr.exception)
コード例 #8
0
ファイル: image.py プロジェクト: pombreda/sanpera
    def write(self, filename, format=None):
        if not self._frames:
            raise EmptyImageError

        with open(filename, "wb") as fh:
            image_info = blank_image_info()
            image_info.file = ffi.cast("FILE *", fh)

            # Force writing to a single file
            image_info.adjoin = lib.MagickTrue

            if format:
                # If the caller provided an explicit format, pass it along
                # Make sure not to overflow the char[]
                # TODO maybe just error out when this happens
                image_info.magick = format.encode('ascii')[:lib.MaxTextExtent]
            elif self._frames[0]._frame.magick[0] == b'\0':
                # Uhoh; no format provided and nothing given by caller
                raise MissingFormatError
            # TODO detect format from filename if explicitly asked to do so

            with self._link_frames(self._frames) as ptr:
                lib.WriteImage(image_info, ptr)
                magick_raise(ptr.exception)
コード例 #9
0
ファイル: filters.py プロジェクト: pombreda/sanpera
def op_number(value):
    return dict(
        op=lib.SANPERA_OP_LOAD_NUMBER,
        color=ffi.NULL,
        number=ffi.cast("double", value),
    )
コード例 #10
0
ファイル: filters.py プロジェクト: pombreda/sanpera
    def __call__(self, *frames, **kwargs):
        channel = kwargs.get('channel', lib.DefaultChannels)
        # TODO force_python should go away and this should become a wrapper for
        # evaluate_python

        # We're gonna be using this a lot, so let's cast it to a C int just
        # once (and get the error early if it's a bogus type)
        c_channel = ffi.cast('ChannelType', channel)

        # TODO any gc concerns in this?
        for f in frames:
            assert isinstance(f, ImageFrame)

        # XXX how to handle frames of different sizes?  gravity?  scaling?  first
        # frame as the master?  hm
        frame = frames[0]

        # TODO does this create a blank image or actually duplicate the pixels??  docs say it actually copies with (0, 0) but the code just refs the same pixel cache?
        # TODO could use an inplace version for, e.g. the SVG-style compose operators
        # TODO also might want a different sized clone!
        with magick_try() as exc:
            new_stack = lib.CloneImage(frame._frame, 0, 0, lib.MagickTrue, exc.ptr)
            exc.check(new_stack == ffi.NULL)

        # TODO: set image to full-color.
        # TODO: work out how this works, how different colorspaces work, and waht the ImageType has to do with anything
        # QUESTION: this doesn't actually do anything.  how does it work?  does it leave indexes populated?  what happens if this isn't done?
        #  if (SetImageStorageClass(fx_image,DirectClass) == MagickFalse) {
        #      InheritException(exception,&fx_image->exception);
        #      fx_image=DestroyImage(fx_image);
        #      return((Image *) NULL);
        #    }

        out_view = lib.AcquireCacheView(new_stack)

        # TODO i need to be a list
        in_view = lib.AcquireCacheView(frame._frame)

        state = FilterState()

        for y in range(frame._frame.rows):

            with magick_try() as exc:
                q = lib.GetCacheViewAuthenticPixels(out_view, 0, y, frame._frame.columns, 1, exc.ptr)
                exc.check(q == ffi.NULL)

            # TODO is this useful who knows
            #fx_indexes=GetCacheViewAuthenticIndexQueue(fx_view);

            for x in range(frame._frame.columns):
                # TODO per-channel things
                # TODO for usage: see line 1453

                #GetMagickPixelPacket(image,&pixel);
                #(void) InterpolateMagickPixelPacket(image,fx_info->view[i],image->interpolate, point.x,point.y,&pixel,exception);

                # Set up state object
                # TODO document that this is reused, or somethin
                state._color = BaseColor._from_pixel(q)
                ret = self.impl(state)

                #q.red = c_api.RoundToQuantum(<c_api.MagickRealType> ret.c_struct.red * c_api.QuantumRange)
                #q.green = c_api.RoundToQuantum(<c_api.MagickRealType> ret.c_struct.green * c_api.QuantumRange)
                #q.blue = c_api.RoundToQuantum(<c_api.MagickRealType> ret.c_struct.blue * c_api.QuantumRange)
                # TODO black, opacity?
                # TODO seems like this should apply to any set of channels, but
                # IM's -fx only understands RGB

                # TODO this is a little invasive, but given that this inner
                # loop runs for every f*****g pixel, i'd like to avoid method
                # calls as much as possible.  even that rgb() can add up
                rgb = ret.rgb()
                lib.sanpera_pixel_from_doubles_channel(q, rgb._array, c_channel)

                # XXX this is actually black
                #  if (((channel & IndexChannel) != 0) && (fx_image->colorspace == CMYKColorspace)) {
                #      (void) FxEvaluateChannelExpression(fx_info[id],IndexChannel,x,y, &alpha,exception);
                #      SetPixelIndex(fx_indexes+x,RoundToQuantum((MagickRealType) QuantumRange*alpha));
                #    }

                q += 1  # q++

            with magick_try() as exc:
                lib.SyncCacheViewAuthenticPixels(in_view, exc.ptr)
                # TODO check exception, return value

        # XXX destroy in_view
        # XXX destroy out_view s

        return Image(new_stack)