mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-27 18:36:17 +03:00
052ea606bf
1) Renamed USE_INLINE to PIL_USE_INLINE to avoid conflicts with other headers/libraries. 2) Replace __WIN32__ and WIN32 with _WIN32 3) Don't define WIN32 when the compiler is MSVC but not on Windows Why would you even... 4) Don't define strcasecmp if you're not even going to use it. 5) Don't include Windows.h with undefs for compilers newer than 1998 everywhere. 6) Don't surpress warnings for MSVC++ 4.0. People still using MSVC++ 4.0 deserve it. 7) Don't include things that are already included in Windows.h
677 lines
18 KiB
C
677 lines
18 KiB
C
/*
|
|
* The Python Imaging Library
|
|
* $Id$
|
|
*
|
|
* incremental decoding adaptor.
|
|
*
|
|
* Copyright (c) 2014 Coriolis Systems Limited
|
|
* Copyright (c) 2014 Alastair Houghton
|
|
*
|
|
*/
|
|
|
|
#include "Imaging.h"
|
|
|
|
/* The idea behind this interface is simple: the actual decoding proceeds in
|
|
a thread, which is run in lock step with the main thread. Whenever the
|
|
ImagingIncrementalCodecRead() call runs short on data, it suspends the
|
|
decoding thread and wakes the main thread. Conversely, the
|
|
ImagingIncrementalCodecPushBuffer() call suspends the main thread and wakes
|
|
the decoding thread, providing a buffer of data.
|
|
|
|
The two threads are never running simultaneously, so there is no need for
|
|
any addition synchronisation measures outside of this file.
|
|
|
|
Note also that we start the thread suspended (on Windows), or make it
|
|
immediately wait (other platforms), so that it's possible to initialise
|
|
things before the thread starts running.
|
|
|
|
This interface is useful to allow PIL to interact efficiently with any
|
|
third-party imaging library that does not support suspendable reads;
|
|
one example is OpenJPEG (which is used for J2K support). The TIFF library
|
|
might also benefit from using this code.
|
|
|
|
Note that if using this module, you want to set handles_eof on your
|
|
decoder to true. Why? Because otherwise ImageFile.load() will abort,
|
|
thinking that the image is truncated, whereas generally you want it to
|
|
pass the EOF condition (0 bytes to read) through to your code. */
|
|
|
|
/* Additional complication: *Some* codecs need to seek; this is fine if
|
|
there is a file descriptor, but if we're buffering data it becomes
|
|
awkward. The incremental adaptor now contains code to handle these
|
|
two cases. */
|
|
|
|
#ifdef _WIN32
|
|
#include <process.h>
|
|
#else
|
|
#include <pthread.h>
|
|
#endif
|
|
|
|
#define DEBUG_INCREMENTAL 0
|
|
|
|
#if DEBUG_INCREMENTAL
|
|
#define DEBUG(...) printf(__VA_ARGS__)
|
|
#else
|
|
#define DEBUG(...)
|
|
#endif
|
|
|
|
struct ImagingIncrementalCodecStruct {
|
|
#ifdef _WIN32
|
|
HANDLE hCodecEvent;
|
|
HANDLE hDataEvent;
|
|
HANDLE hThread;
|
|
#else
|
|
pthread_mutex_t start_mutex;
|
|
pthread_cond_t start_cond;
|
|
pthread_mutex_t codec_mutex;
|
|
pthread_cond_t codec_cond;
|
|
pthread_mutex_t data_mutex;
|
|
pthread_cond_t data_cond;
|
|
pthread_t thread;
|
|
#endif
|
|
ImagingIncrementalCodecEntry entry;
|
|
Imaging im;
|
|
ImagingCodecState state;
|
|
struct {
|
|
int fd;
|
|
UINT8 *buffer; /* Base of buffer */
|
|
UINT8 *ptr; /* Current pointer in buffer */
|
|
UINT8 *top; /* Highest point in buffer we've used */
|
|
UINT8 *end; /* End of buffer */
|
|
} stream;
|
|
int read_or_write;
|
|
int seekable;
|
|
int started;
|
|
int result;
|
|
};
|
|
|
|
static void flush_stream(ImagingIncrementalCodec codec);
|
|
|
|
#if _WIN32
|
|
static unsigned int __stdcall
|
|
codec_thread(void *ptr)
|
|
{
|
|
ImagingIncrementalCodec codec = (ImagingIncrementalCodec)ptr;
|
|
|
|
DEBUG("Entering thread\n");
|
|
|
|
codec->result = codec->entry(codec->im, codec->state, codec);
|
|
|
|
DEBUG("Leaving thread (%d)\n", codec->result);
|
|
|
|
flush_stream(codec);
|
|
|
|
SetEvent(codec->hCodecEvent);
|
|
|
|
return 0;
|
|
}
|
|
#else
|
|
static void *
|
|
codec_thread(void *ptr)
|
|
{
|
|
ImagingIncrementalCodec codec = (ImagingIncrementalCodec)ptr;
|
|
|
|
DEBUG("Entering thread\n");
|
|
|
|
codec->result = codec->entry(codec->im, codec->state, codec);
|
|
|
|
DEBUG("Leaving thread (%d)\n", codec->result);
|
|
|
|
flush_stream(codec);
|
|
|
|
pthread_mutex_lock(&codec->codec_mutex);
|
|
pthread_cond_signal(&codec->codec_cond);
|
|
pthread_mutex_unlock(&codec->codec_mutex);
|
|
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
flush_stream(ImagingIncrementalCodec codec)
|
|
{
|
|
UINT8 *buffer;
|
|
size_t bytes;
|
|
|
|
/* This is to flush data from the write buffer for a seekable write
|
|
codec. */
|
|
if (codec->read_or_write != INCREMENTAL_CODEC_WRITE
|
|
|| codec->state->errcode != IMAGING_CODEC_END
|
|
|| !codec->seekable
|
|
|| codec->stream.fd >= 0)
|
|
return;
|
|
|
|
DEBUG("flushing data\n");
|
|
|
|
buffer = codec->stream.buffer;
|
|
bytes = codec->stream.ptr - codec->stream.buffer;
|
|
|
|
codec->state->errcode = 0;
|
|
codec->seekable = INCREMENTAL_CODEC_NOT_SEEKABLE;
|
|
codec->stream.buffer = codec->stream.ptr = codec->stream.end
|
|
= codec->stream.top = NULL;
|
|
|
|
ImagingIncrementalCodecWrite(codec, buffer, bytes);
|
|
|
|
codec->state->errcode = IMAGING_CODEC_END;
|
|
codec->result = (int)ImagingIncrementalCodecBytesInBuffer(codec);
|
|
|
|
free(buffer);
|
|
}
|
|
|
|
/**
|
|
* Create a new incremental codec */
|
|
ImagingIncrementalCodec
|
|
ImagingIncrementalCodecCreate(ImagingIncrementalCodecEntry codec_entry,
|
|
Imaging im,
|
|
ImagingCodecState state,
|
|
int read_or_write,
|
|
int seekable,
|
|
int fd)
|
|
{
|
|
ImagingIncrementalCodec codec = (ImagingIncrementalCodec)malloc(sizeof(struct ImagingIncrementalCodecStruct));
|
|
|
|
codec->entry = codec_entry;
|
|
codec->im = im;
|
|
codec->state = state;
|
|
codec->result = 0;
|
|
codec->stream.fd = fd;
|
|
codec->stream.buffer = codec->stream.ptr = codec->stream.end
|
|
= codec->stream.top = NULL;
|
|
codec->started = 0;
|
|
codec->seekable = seekable;
|
|
codec->read_or_write = read_or_write;
|
|
|
|
if (fd >= 0)
|
|
lseek(fd, 0, SEEK_SET);
|
|
|
|
/* System specific set-up */
|
|
#if _WIN32
|
|
codec->hCodecEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
if (!codec->hCodecEvent) {
|
|
free(codec);
|
|
return NULL;
|
|
}
|
|
|
|
codec->hDataEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
if (!codec->hDataEvent) {
|
|
CloseHandle(codec->hCodecEvent);
|
|
free(codec);
|
|
return NULL;
|
|
}
|
|
|
|
codec->hThread = _beginthreadex(NULL, 0, codec_thread, codec,
|
|
CREATE_SUSPENDED, NULL);
|
|
|
|
if (!codec->hThread) {
|
|
CloseHandle(codec->hCodecEvent);
|
|
CloseHandle(codec->hDataEvent);
|
|
free(codec);
|
|
return NULL;
|
|
}
|
|
#else
|
|
if (pthread_mutex_init(&codec->start_mutex, NULL)) {
|
|
free (codec);
|
|
return NULL;
|
|
}
|
|
|
|
if (pthread_mutex_init(&codec->codec_mutex, NULL)) {
|
|
pthread_mutex_destroy(&codec->start_mutex);
|
|
free(codec);
|
|
return NULL;
|
|
}
|
|
|
|
if (pthread_mutex_init(&codec->data_mutex, NULL)) {
|
|
pthread_mutex_destroy(&codec->start_mutex);
|
|
pthread_mutex_destroy(&codec->codec_mutex);
|
|
free(codec);
|
|
return NULL;
|
|
}
|
|
|
|
if (pthread_cond_init(&codec->start_cond, NULL)) {
|
|
pthread_mutex_destroy(&codec->start_mutex);
|
|
pthread_mutex_destroy(&codec->codec_mutex);
|
|
pthread_mutex_destroy(&codec->data_mutex);
|
|
free(codec);
|
|
return NULL;
|
|
}
|
|
|
|
if (pthread_cond_init(&codec->codec_cond, NULL)) {
|
|
pthread_mutex_destroy(&codec->start_mutex);
|
|
pthread_mutex_destroy(&codec->codec_mutex);
|
|
pthread_mutex_destroy(&codec->data_mutex);
|
|
pthread_cond_destroy(&codec->start_cond);
|
|
free(codec);
|
|
return NULL;
|
|
}
|
|
|
|
if (pthread_cond_init(&codec->data_cond, NULL)) {
|
|
pthread_mutex_destroy(&codec->start_mutex);
|
|
pthread_mutex_destroy(&codec->codec_mutex);
|
|
pthread_mutex_destroy(&codec->data_mutex);
|
|
pthread_cond_destroy(&codec->start_cond);
|
|
pthread_cond_destroy(&codec->codec_cond);
|
|
free(codec);
|
|
return NULL;
|
|
}
|
|
|
|
if (pthread_create(&codec->thread, NULL, codec_thread, codec)) {
|
|
pthread_mutex_destroy(&codec->start_mutex);
|
|
pthread_mutex_destroy(&codec->codec_mutex);
|
|
pthread_mutex_destroy(&codec->data_mutex);
|
|
pthread_cond_destroy(&codec->start_cond);
|
|
pthread_cond_destroy(&codec->codec_cond);
|
|
pthread_cond_destroy(&codec->data_cond);
|
|
free(codec);
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
return codec;
|
|
}
|
|
|
|
/**
|
|
* Destroy an incremental codec */
|
|
void
|
|
ImagingIncrementalCodecDestroy(ImagingIncrementalCodec codec)
|
|
{
|
|
DEBUG("destroying\n");
|
|
|
|
if (!codec->started) {
|
|
#ifdef _WIN32
|
|
ResumeThread(codec->hThread);
|
|
#else
|
|
pthread_cond_signal(&codec->start_cond);
|
|
#endif
|
|
codec->started = 1;
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
pthread_mutex_lock(&codec->data_mutex);
|
|
#endif
|
|
|
|
if (codec->seekable && codec->stream.fd < 0)
|
|
free (codec->stream.buffer);
|
|
|
|
codec->stream.buffer = codec->stream.ptr = codec->stream.end
|
|
= codec->stream.top = NULL;
|
|
|
|
#ifdef _WIN32
|
|
SetEvent(codec->hDataEvent);
|
|
|
|
WaitForSingleObject(codec->hThread, INFINITE);
|
|
|
|
CloseHandle(codec->hThread);
|
|
CloseHandle(codec->hCodecEvent);
|
|
CloseHandle(codec->hDataEvent);
|
|
#else
|
|
pthread_cond_signal(&codec->data_cond);
|
|
pthread_mutex_unlock(&codec->data_mutex);
|
|
|
|
pthread_join(codec->thread, NULL);
|
|
|
|
pthread_mutex_destroy(&codec->start_mutex);
|
|
pthread_mutex_destroy(&codec->codec_mutex);
|
|
pthread_mutex_destroy(&codec->data_mutex);
|
|
pthread_cond_destroy(&codec->start_cond);
|
|
pthread_cond_destroy(&codec->codec_cond);
|
|
pthread_cond_destroy(&codec->data_cond);
|
|
#endif
|
|
free (codec);
|
|
}
|
|
|
|
/**
|
|
* Push a data buffer for an incremental codec */
|
|
int
|
|
ImagingIncrementalCodecPushBuffer(ImagingIncrementalCodec codec,
|
|
UINT8 *buf, int bytes)
|
|
{
|
|
if (!codec->started) {
|
|
DEBUG("starting\n");
|
|
|
|
#ifdef _WIN32
|
|
ResumeThread(codec->hThread);
|
|
#else
|
|
pthread_cond_signal(&codec->start_cond);
|
|
#endif
|
|
codec->started = 1;
|
|
|
|
/* Wait for the thread to ask for data */
|
|
#ifdef _WIN32
|
|
WaitForSingleObject(codec->hCodecEvent, INFINITE);
|
|
#else
|
|
pthread_mutex_lock(&codec->codec_mutex);
|
|
pthread_cond_wait(&codec->codec_cond, &codec->codec_mutex);
|
|
pthread_mutex_unlock(&codec->codec_mutex);
|
|
#endif
|
|
if (codec->result < 0) {
|
|
DEBUG("got result %d\n", codec->result);
|
|
|
|
return codec->result;
|
|
}
|
|
}
|
|
|
|
/* Codecs using an fd don't need data, so when we get here, we're done */
|
|
if (codec->stream.fd >= 0) {
|
|
DEBUG("got result %d\n", codec->result);
|
|
|
|
return codec->result;
|
|
}
|
|
|
|
DEBUG("providing %p, %d\n", buf, bytes);
|
|
|
|
#ifndef _WIN32
|
|
pthread_mutex_lock(&codec->data_mutex);
|
|
#endif
|
|
|
|
if (codec->read_or_write == INCREMENTAL_CODEC_READ
|
|
&& codec->seekable && codec->stream.fd < 0) {
|
|
/* In this specific case, we append to a buffer we allocate ourselves */
|
|
size_t old_size = codec->stream.end - codec->stream.buffer;
|
|
size_t new_size = codec->stream.end - codec->stream.buffer + bytes;
|
|
UINT8 *new = (UINT8 *)realloc (codec->stream.buffer, new_size);
|
|
|
|
if (!new) {
|
|
codec->state->errcode = IMAGING_CODEC_MEMORY;
|
|
#ifndef _WIN32
|
|
pthread_mutex_unlock(&codec->data_mutex);
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
codec->stream.ptr = codec->stream.ptr - codec->stream.buffer + new;
|
|
codec->stream.end = new + new_size;
|
|
codec->stream.buffer = new;
|
|
|
|
memcpy(new + old_size, buf, bytes);
|
|
} else {
|
|
codec->stream.buffer = codec->stream.ptr = buf;
|
|
codec->stream.end = buf + bytes;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
SetEvent(codec->hDataEvent);
|
|
WaitForSingleObject(codec->hCodecEvent, INFINITE);
|
|
#else
|
|
pthread_cond_signal(&codec->data_cond);
|
|
pthread_mutex_unlock(&codec->data_mutex);
|
|
|
|
pthread_mutex_lock(&codec->codec_mutex);
|
|
pthread_cond_wait(&codec->codec_cond, &codec->codec_mutex);
|
|
pthread_mutex_unlock(&codec->codec_mutex);
|
|
#endif
|
|
|
|
DEBUG("got result %d\n", codec->result);
|
|
|
|
return codec->result;
|
|
}
|
|
|
|
size_t
|
|
ImagingIncrementalCodecBytesInBuffer(ImagingIncrementalCodec codec)
|
|
{
|
|
return codec->stream.ptr - codec->stream.buffer;
|
|
}
|
|
|
|
ssize_t
|
|
ImagingIncrementalCodecRead(ImagingIncrementalCodec codec,
|
|
void *buffer, size_t bytes)
|
|
{
|
|
UINT8 *ptr = (UINT8 *)buffer;
|
|
size_t done = 0;
|
|
|
|
if (codec->read_or_write == INCREMENTAL_CODEC_WRITE) {
|
|
DEBUG("attempt to read from write codec\n");
|
|
return -1;
|
|
}
|
|
|
|
DEBUG("reading (want %llu bytes)\n", (unsigned long long)bytes);
|
|
|
|
if (codec->stream.fd >= 0) {
|
|
ssize_t ret = read(codec->stream.fd, buffer, bytes);
|
|
DEBUG("read %lld bytes from fd\n", (long long)ret);
|
|
return ret;
|
|
}
|
|
|
|
#ifndef _WIN32
|
|
pthread_mutex_lock(&codec->data_mutex);
|
|
#endif
|
|
while (bytes) {
|
|
size_t todo = bytes;
|
|
size_t remaining = codec->stream.end - codec->stream.ptr;
|
|
|
|
if (!remaining) {
|
|
DEBUG("waiting for data\n");
|
|
|
|
#ifndef _WIN32
|
|
pthread_mutex_lock(&codec->codec_mutex);
|
|
#endif
|
|
codec->result = (int)(codec->stream.ptr - codec->stream.buffer);
|
|
#if _WIN32
|
|
SetEvent(codec->hCodecEvent);
|
|
WaitForSingleObject(codec->hDataEvent, INFINITE);
|
|
#else
|
|
pthread_cond_signal(&codec->codec_cond);
|
|
pthread_mutex_unlock(&codec->codec_mutex);
|
|
pthread_cond_wait(&codec->data_cond, &codec->data_mutex);
|
|
#endif
|
|
|
|
remaining = codec->stream.end - codec->stream.ptr;
|
|
codec->stream.top = codec->stream.end;
|
|
|
|
DEBUG("got %llu bytes\n", (unsigned long long)remaining);
|
|
}
|
|
|
|
if (todo > remaining)
|
|
todo = remaining;
|
|
|
|
if (!todo)
|
|
break;
|
|
|
|
memcpy (ptr, codec->stream.ptr, todo);
|
|
codec->stream.ptr += todo;
|
|
bytes -= todo;
|
|
done += todo;
|
|
ptr += todo;
|
|
}
|
|
#ifndef _WIN32
|
|
pthread_mutex_unlock(&codec->data_mutex);
|
|
#endif
|
|
|
|
DEBUG("read total %llu bytes\n", (unsigned long long)done);
|
|
|
|
return done;
|
|
}
|
|
|
|
off_t
|
|
ImagingIncrementalCodecSkip(ImagingIncrementalCodec codec,
|
|
off_t bytes)
|
|
{
|
|
off_t done = 0;
|
|
|
|
DEBUG("skipping (want %llu bytes)\n", (unsigned long long)bytes);
|
|
|
|
/* In write mode, explicitly fill with zeroes */
|
|
if (codec->read_or_write == INCREMENTAL_CODEC_WRITE) {
|
|
static const UINT8 zeroes[256] = { 0 };
|
|
off_t done = 0;
|
|
while (bytes) {
|
|
size_t todo = (size_t)(bytes > 256 ? 256 : bytes);
|
|
ssize_t written = ImagingIncrementalCodecWrite(codec, zeroes, todo);
|
|
if (written <= 0)
|
|
break;
|
|
done += written;
|
|
bytes -= written;
|
|
}
|
|
return done;
|
|
}
|
|
|
|
if (codec->stream.fd >= 0)
|
|
return lseek(codec->stream.fd, bytes, SEEK_CUR);
|
|
|
|
#ifndef _WIN32
|
|
pthread_mutex_lock(&codec->data_mutex);
|
|
#endif
|
|
while (bytes) {
|
|
off_t todo = bytes;
|
|
off_t remaining = codec->stream.end - codec->stream.ptr;
|
|
|
|
if (!remaining) {
|
|
DEBUG("waiting for data\n");
|
|
|
|
#ifndef _WIN32
|
|
pthread_mutex_lock(&codec->codec_mutex);
|
|
#endif
|
|
codec->result = (int)(codec->stream.ptr - codec->stream.buffer);
|
|
#if _WIN32
|
|
SetEvent(codec->hCodecEvent);
|
|
WaitForSingleObject(codec->hDataEvent, INFINITE);
|
|
#else
|
|
pthread_cond_signal(&codec->codec_cond);
|
|
pthread_mutex_unlock(&codec->codec_mutex);
|
|
pthread_cond_wait(&codec->data_cond, &codec->data_mutex);
|
|
#endif
|
|
|
|
remaining = codec->stream.end - codec->stream.ptr;
|
|
}
|
|
|
|
if (todo > remaining)
|
|
todo = remaining;
|
|
|
|
if (!todo)
|
|
break;
|
|
|
|
codec->stream.ptr += todo;
|
|
bytes -= todo;
|
|
done += todo;
|
|
}
|
|
#ifndef _WIN32
|
|
pthread_mutex_unlock(&codec->data_mutex);
|
|
#endif
|
|
|
|
DEBUG("skipped total %llu bytes\n", (unsigned long long)done);
|
|
|
|
return done;
|
|
}
|
|
|
|
ssize_t
|
|
ImagingIncrementalCodecWrite(ImagingIncrementalCodec codec,
|
|
const void *buffer, size_t bytes)
|
|
{
|
|
const UINT8 *ptr = (const UINT8 *)buffer;
|
|
size_t done = 0;
|
|
|
|
if (codec->read_or_write == INCREMENTAL_CODEC_READ) {
|
|
DEBUG("attempt to write from read codec\n");
|
|
return -1;
|
|
}
|
|
|
|
DEBUG("write (have %llu bytes)\n", (unsigned long long)bytes);
|
|
|
|
if (codec->stream.fd >= 0)
|
|
return write(codec->stream.fd, buffer, bytes);
|
|
|
|
#ifndef _WIN32
|
|
pthread_mutex_lock(&codec->data_mutex);
|
|
#endif
|
|
while (bytes) {
|
|
size_t todo = bytes;
|
|
size_t remaining = codec->stream.end - codec->stream.ptr;
|
|
|
|
if (!remaining) {
|
|
if (codec->seekable && codec->stream.fd < 0) {
|
|
/* In this case, we maintain the stream buffer ourselves */
|
|
size_t old_size = codec->stream.top - codec->stream.buffer;
|
|
size_t new_size = (old_size + bytes + 65535) & ~65535;
|
|
UINT8 *new = (UINT8 *)realloc(codec->stream.buffer, new_size);
|
|
|
|
if (!new) {
|
|
codec->state->errcode = IMAGING_CODEC_MEMORY;
|
|
#ifndef _WIN32
|
|
pthread_mutex_unlock(&codec->data_mutex);
|
|
#endif
|
|
return done == 0 ? -1 : done;
|
|
}
|
|
|
|
codec->stream.ptr = codec->stream.ptr - codec->stream.buffer + new;
|
|
codec->stream.buffer = new;
|
|
codec->stream.end = new + new_size;
|
|
codec->stream.top = new + old_size;
|
|
} else {
|
|
DEBUG("waiting for space\n");
|
|
|
|
#ifndef _WIN32
|
|
pthread_mutex_lock(&codec->codec_mutex);
|
|
#endif
|
|
codec->result = (int)(codec->stream.ptr - codec->stream.buffer);
|
|
#if _WIN32
|
|
SetEvent(codec->hCodecEvent);
|
|
WaitForSingleObject(codec->hDataEvent, INFINITE);
|
|
#else
|
|
pthread_cond_signal(&codec->codec_cond);
|
|
pthread_mutex_unlock(&codec->codec_mutex);
|
|
pthread_cond_wait(&codec->data_cond, &codec->data_mutex);
|
|
#endif
|
|
}
|
|
|
|
remaining = codec->stream.end - codec->stream.ptr;
|
|
|
|
DEBUG("got %llu bytes\n", (unsigned long long)remaining);
|
|
}
|
|
if (todo > remaining)
|
|
todo = remaining;
|
|
|
|
if (!todo)
|
|
break;
|
|
|
|
memcpy (codec->stream.ptr, ptr, todo);
|
|
codec->stream.ptr += todo;
|
|
bytes -= todo;
|
|
done += todo;
|
|
ptr += todo;
|
|
}
|
|
|
|
if (codec->stream.ptr > codec->stream.top)
|
|
codec->stream.top = codec->stream.ptr;
|
|
|
|
#ifndef _WIN32
|
|
pthread_mutex_unlock(&codec->data_mutex);
|
|
#endif
|
|
|
|
DEBUG("wrote total %llu bytes\n", (unsigned long long)done);
|
|
|
|
return done;
|
|
}
|
|
|
|
off_t
|
|
ImagingIncrementalCodecSeek(ImagingIncrementalCodec codec,
|
|
off_t bytes)
|
|
{
|
|
off_t buffered;
|
|
|
|
DEBUG("seeking (going to %llu bytes)\n", (unsigned long long)bytes);
|
|
|
|
if (codec->stream.fd >= 0)
|
|
return lseek(codec->stream.fd, bytes, SEEK_SET);
|
|
|
|
if (!codec->seekable) {
|
|
DEBUG("attempt to seek non-seekable stream\n");
|
|
return -1;
|
|
}
|
|
|
|
if (bytes < 0) {
|
|
DEBUG("attempt to seek before stream start\n");
|
|
return -1;
|
|
}
|
|
|
|
buffered = codec->stream.top - codec->stream.buffer;
|
|
|
|
if (bytes <= buffered) {
|
|
DEBUG("seek within buffer\n");
|
|
codec->stream.ptr = codec->stream.buffer + bytes;
|
|
return bytes;
|
|
}
|
|
|
|
return buffered + ImagingIncrementalCodecSkip(codec, bytes - buffered);
|
|
}
|