Dcraw con caché y soporte para Rawzor
Añadiendo esta única línea (en el sitio adecuado) al código fuente de dcraw:
#include "rn_cache.c"
conseguimos implementar un sistema de caché para la lectura y decodificación del archivo raw que acelera considerablemente la carga y, de paso, implementamos soporte para archivos raw comprimidos con Rawzor.
Los resultados aplicados a mi colección de raws (tiempos de carga y decodificación en segundos):
El tiempo conjunto empleado en obtener un revelado rápido (opción -h) para toda mi colección de raws ha pasado de 27,0 a 18,4 segundos, es decir, se ha visto reducido en más de 8 segundos y medio (una mejora de un 32%). A la hora de obtener los thumbnails de un directorio de archivos raw es un tiempo considerable.
No modificar practicamente nada el código de Coffin es importante para poder sacar actualizaciones tan rápido como él saque una nueva versión. Podéis descargar desde aquí binarios de dcraw para varias plataformas con y sin el caché.
Como se ve en la gráfica hay un par de casos en los que el caché aumenta el tiempo de carga. Se trata de los archivos de las Sony comprimidos y los archivos de FinePix. En la versión que liberaré del código (aquí se trataba de ilustrar el asunto), los archivos de estas cámaras no usarán el sistema de caché.
El código del caché, rn_cache.c:
// To the extent possible under law, Manuel Llorens <manuelllorens@gmail.com>
// has waived all copyright and related or neighboring rights to this work.
// This code is licensed under CC0 v1.0, see license information at
// http://creativecommons.org/publicdomain/zero/1.0/
#include "rwz_sdk.h"
// In-memory file struct
typedef struct{
int id;
unsigned char *ptr;
size_t size;
long int position;
} rn_FILE;
// Gets an rn_FILE pointer from a FILE pointer
rn_FILE *rn_get_rn_FILE(const FILE *stream){
rn_FILE *mem_stream;
mem_stream=(rn_FILE *)stream;
if(mem_stream->id==0x7261776e) return mem_stream; else return NULL;
}
// Replaces fopen function. If the file is opened for binary reading
// it is loaded in memory and a pointer to this memory file is returned
FILE *rn_fopen (const char *filename, const char *mode){
FILE *stream;
rn_FILE *mem_stream;
int rwz_status,rwz_size;
int tiff_order;
unsigned char *rwz_buffer;
// Open the file and return NULL if not success
stream=fopen(filename,mode);
if(stream==NULL) return NULL;
// Only use in-memory file for reading binary files
if(strcmp(mode,"rb")) return stream;
// Create a new rn_FILE struct
mem_stream=(rn_FILE *)malloc(sizeof(rn_FILE));
merror(mem_stream,"rawness rn_fopen");
// Get file length and reserve memory
fseek(stream,0,SEEK_END);
mem_stream->size=ftell(stream);
// If size is 0 return NULL
if(mem_stream->size==0) return NULL;
// Alloc memory for the in-memory file
mem_stream->ptr=(unsigned char *)malloc(mem_stream->size);
merror(mem_stream->ptr,"rawness rn_fopen");
// Read the full file into memory
fseek(stream,0,SEEK_SET);
fread(mem_stream->ptr,mem_stream->size,1,stream);
// Close the file
fclose(stream);
// ********************
// RAWZOR decompression
// Is rawzor file?
rwz_status=m_rwz_check((char *)mem_stream->ptr,mem_stream->size,&rwz_size);
if (!rwz_status){
// Yes!
if (verbose)
fprintf(stderr,_("Decompressing Rawzor file...n"),rwz_status);
// Decompress file
rwz_buffer=(unsigned char *)malloc(rwz_size);
merror(mem_stream->ptr,"rawness rn_fopen");
rwz_status=m_rwz_decompress((char *)mem_stream->ptr,mem_stream->size,(char *)rwz_buffer,rwz_size);
if (rwz_status){
// Wrong rwz file
fprintf(stderr,_("Not a valid Rawzor filen"),rwz_status);
}else{
// Rawzor file decompressed
free (mem_stream->ptr);
mem_stream->ptr=rwz_buffer;
mem_stream->size=rwz_size;
}
}
// Yes, but wrong version
if (rwz_status==2){
fprintf(stderr,_("The input rwz file needs a newer version of Rawzor SDKn"));
return NULL;
}
// ********************
// Return the rn_FILE
mem_stream->id=0x7261776e;
mem_stream->position=0;
return (FILE *)mem_stream;
}
// Replaces fclose function. If the file pointer is a memory pointer
// it releases the memory. The real file was closed in fopen
int rn_fclose (FILE *stream){
rn_FILE *mem_stream;
if(stream!=NULL){
// Check if the FILE pointer is a in-memory pointer or not
// if not call original fclose and return
mem_stream=rn_get_rn_FILE(stream);
if(mem_stream==NULL) return fclose(stream);
// Release the in-memory file
free (mem_stream->ptr);
free (mem_stream);
// Return success
return 0;
}else{
// Return fail
return -1;
}
}
// Replaces fread function. It copies from the memory buffer instead
// of reading the file from disk
size_t rn_fread (void *ptr, size_t size, size_t count, FILE *stream){
rn_FILE *mem_stream;
// Check if the FILE pointer is a in-memory pointer or not
// if not call original fclose and return
mem_stream=rn_get_rn_FILE(stream);
if(mem_stream==NULL) return fread(ptr,size,count,stream);
// Calculate the correct size (do not overflow)
if(size*count>mem_stream->size-mem_stream->position){
size=mem_stream->size-mem_stream->position;
}else{
size*=count;
}
// Do the memcpy from the in-memory file
memcpy(ptr,mem_stream->ptr+mem_stream->position,size);
// Increase position
mem_stream->position+=size;
return size;
}
// Replaces getc function. It gets the int in the pointer position
int rn_getc (FILE *stream){
rn_FILE *mem_stream;
int r;
// Check if the FILE pointer is a in-memory pointer or not
// if not call original getc and return
mem_stream=rn_get_rn_FILE(stream);
if(mem_stream==NULL) return getc(stream);
// Get int value in current position and return
if(mem_stream->position<=mem_stream->size){
r=(mem_stream->ptr+mem_stream->position)[0];
mem_stream->position++;
return r;
}else return EOF;
}
// Replaces feof function. If checks for in-memory end of file
int rn_feof (FILE *stream){
rn_FILE *mem_stream;
// Check if the FILE pointer is a in-memory pointer or not
// if not call original feof and return
mem_stream=rn_get_rn_FILE(stream);
if(mem_stream==NULL) return feof(stream);
// Check and return end of file
if(mem_stream->position<mem_stream->size) return 0; else return EOF;
}
long int rn_fseek (FILE *stream, long int offset, int origin){
rn_FILE *mem_stream;
// Check if the FILE pointer is a in-memory pointer or not
// if not call original fseek and return
mem_stream=rn_get_rn_FILE(stream);
if(mem_stream==NULL) return fseek(stream,offset,origin);
// Calculate the new position
switch(origin){
case SEEK_SET:
mem_stream->position=offset;
break;
case SEEK_CUR:
mem_stream->position+=offset;
break;
case SEEK_END:
mem_stream->position=mem_stream->size+offset;
}
// Check the new position
if(mem_stream->position<mem_stream->size) return 0; else return EOF;
}
long int rn_ftell (FILE *stream){
rn_FILE *mem_stream;
// Check if the FILE pointer is a in-memory pointer or not
// if not call original fseek and return
mem_stream=rn_get_rn_FILE(stream);
if(mem_stream==NULL) return ftell(stream);
// Return position
return mem_stream->position;
}
// This function is intended to work with ONLY ONE argument, as
// original Coffin's code only uses it that way. If you need to
// call fscanf with a real file and more than one argument, you
// must use real_fscanf instead.
//
// Beware it will neither check for file EOF.
int rn_fscanf(FILE *stream, const char *format, void *ptr){
rn_FILE *mem_stream;
// Check if the FILE pointer is a in-memory pointer or not
// if not call original fscanf with one argument and return
mem_stream=rn_get_rn_FILE(stream);
if(mem_stream==NULL) return fscanf(stream,format,ptr);
// Use sscanf instead of fscanf
sscanf((const char *)mem_stream->ptr+mem_stream->position,format,ptr);
return 1;
}
char *rn_fgets(char *str, int n, FILE *stream){
rn_FILE *mem_stream;
int i;
// Check if the FILE pointer is a in-memory pointer or not
// if not call original fgets and return
mem_stream=rn_get_rn_FILE(stream);
if(mem_stream==NULL) return fgets(str,n,stream);
// Check for n size
if(mem_stream->position+n>mem_stream->size) n=mem_stream->size-mem_stream->position;
// Get the string
i=0;
while(i<n){
str[i]=mem_stream->ptr[mem_stream->position++];
if((str[i]==0x13)||(str[i]==0x00)) break;
i++;
}
mem_stream->position--;
str[i]=0x00;
return str;
}
// Define new file functions for in-memory raw file opening
// This is needed for in-memory rawzor support
#define real_fscanf fscanf
#define fopen rn_fopen
#define fclose rn_fclose
#define fread rn_fread
#define getc rn_getc
#define fgetc rn_getc
#define feof rn_feof
#define fseek rn_fseek
#define ftell rn_ftell
#define fscanf rn_fscanf
#define fgets rn_fgets
Si quieres descargar una versión de dcraw con estas mejoras, la tienes aquí.
Related posts:

Vallabha, do you mean compiling the original Coffin’s source code?
In a console window go to the directory where you have downloaded dcraw source code (dcraw.c) and type:
gcc -o dcraw -O4 dcraw.c -lm -DNO_JPEG -DNO_LCMSThis will give you a dcraw executable (execute with
./dcraw) with no LCMS support, the most basic one.Is this enough?
If you want to compile dcraw with my cache code you must add the include line (
#include "rn_cache.c", the semicolon was a typo) in the right position (try to guest where! you will learn more than if I just tell you) and leave my code in a file called rn_cache.c in the same directory that dcraw.c. That should do the work.Comentario by Manuel Llorens García — 11 noviembre, 2010 - 14:54
I want to compile and run it in ubuntu linux platform.
Comentario by Vallabha — 11 noviembre, 2010 - 13:17
I am no able to compile the dcraw source will you please give me steps to compile and run.
Comentario by Vallabha — 11 noviembre, 2010 - 13:14
I have planned and article about compiling dcraw in Windows, Linux and MacOS.
In the meanwhile, your problems are arising when compiling dcraw or when trying to compile dcraw with my cache code?
Could you post the compiler messages here?
Comentario by Manuel Llorens García — 1 noviembre, 2010 - 20:02
I am having some trouble compiling your code along with dcraw. Could you post instructions for how to compile your code along with dcraw using gcc using Mac OS X?
Thanks and keep up the great work.
Comentario by Dan — 1 noviembre, 2010 - 01:52
You have the cache source code in this page, and full Coffin’s dcraw source code in his page. It has no sense to publish full source code since it will change whenever Coffin changes his code.
Do you want instructions on how to compile the whole thing?
Comentario by Manuel Llorens García — 27 septiembre, 2010 - 17:50
is there complete source code available? thanks.
Comentario by andrey — 23 septiembre, 2010 - 01:08
Thank you very much for such kind words.
Comentario by Manuel Llorens García — 11 abril, 2010 - 00:18
This is great. Really nice post. Very Informative and helpful post. thank you.
Dcraw with cache and Rawzor support – rawness | digital photography science
Comentario by Instalacion De Redes — 10 abril, 2010 - 00:39
[...] Manuel Llorens has released really fast precompiled dcraw binaries with Rawzor support. The speed results are really impressive in our tests and he has detailed charts on his website here and here. [...]
ComentarioPingback by Fast Precompiled dcraw Binaries with Rawzor Support - Official Rawzor Blog — 13 marzo, 2010 - 15:51