diff --git a/CMakeLists.txt b/CMakeLists.txt index 0411ffc9..70d43585 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,6 +80,7 @@ option(WITH_TIFF "Use TIFFs as a cache backend" OFF) option(WITH_TIFF_WRITE_SUPPORT "Enable (experimental) support for writable TIFF cache backends" OFF) option(WITH_GEOTIFF "Allow GeoTIFF metadata creation for TIFF cache backends" OFF) option(WITH_PCRE "Use PCRE for regex tests" OFF) +option(WITH_GIF "Enable GIF read support for remote sources" OFF) option(WITH_MAPSERVER "Enable (experimental) support for the mapserver library" OFF) find_package(PNG) @@ -97,6 +98,17 @@ if(JPEG_FOUND) else(JPEG_FOUND) endif(JPEG_FOUND) +if(WITH_GIF) + find_package(GIF) + if(GIF_FOUND) + include_directories(${GIF_INCLUDE_DIR}) + target_link_libraries(mapcache ${GIF_LIBRARY}) + set (USE_GIF 1) + else(GIF_FOUND) + report_optional_not_found(GIF) + endif(GIF_FOUND) +endif (WITH_GIF) + find_package(CURL) if(CURL_FOUND) include_directories(${CURL_INCLUDE_DIR}) @@ -275,6 +287,7 @@ status_optional_component("TIFF" "${USE_TIFF}" "${TIFF_LIBRARY}") status_optional_component("GeoTIFF" "${USE_GEOTIFF}" "${GEOTIFF_LIBRARY}") status_optional_component("Experimental TIFF write support" "${USE_TIFF_WRITE}" "${TIFF_LIBRARY}") status_optional_component("PCRE" "${USE_PCRE}" "${PCRE_LIBRARY}") +status_optional_component("GIF read support" "${USE_GIF}" "${GIF_LIBRARY}") status_optional_component("Experimental mapserver support" "${USE_MAPSERVER}" "${MAPSERVER_LIBRARY}") INSTALL(TARGETS mapcache DESTINATION ${CMAKE_INSTALL_LIBDIR}) diff --git a/include/mapcache-config.h.in b/include/mapcache-config.h.in index 537afca1..e93ab0d6 100644 --- a/include/mapcache-config.h.in +++ b/include/mapcache-config.h.in @@ -10,6 +10,7 @@ #cmakedefine USE_TIFF_WRITE 1 #cmakedefine USE_GEOTIFF 1 #cmakedefine USE_PCRE 1 +#cmakedefine USE_GIF 1 #cmakedefine USE_MAPSERVER 1 #cmakedefine HAVE_STRNCASECMP 1 diff --git a/include/mapcache.h b/include/mapcache.h index ab3f30b6..302674e6 100644 --- a/include/mapcache.h +++ b/include/mapcache.h @@ -87,6 +87,9 @@ typedef struct mapcache_image_format_mixed mapcache_image_format_mixed; typedef struct mapcache_image_format_png mapcache_image_format_png; typedef struct mapcache_image_format_png_q mapcache_image_format_png_q; typedef struct mapcache_image_format_jpeg mapcache_image_format_jpeg; +#ifdef USE_GIF +typedef struct mapcache_image_format_gif mapcache_image_format_gif; +#endif // USE_GIF typedef struct mapcache_cfg mapcache_cfg; typedef struct mapcache_tileset mapcache_tileset; typedef struct mapcache_cache mapcache_cache; @@ -928,6 +931,9 @@ void mapcache_service_dispatch_request(mapcache_context *ctx, typedef enum { GC_UNKNOWN, GC_PNG, GC_JPEG +#ifdef USE_GIF +, GC_GIF +#endif // USE_GIF } mapcache_image_format_type; typedef enum { @@ -1743,6 +1749,30 @@ void mapcache_imageio_decode_to_image(mapcache_context *ctx, mapcache_buffer *bu /** @} */ +#ifdef USE_GIF +/**\defgroup imageio_gif GIF Image IO + * \ingroup imageio */ +/** @{ */ + +/** + * @param r + * @param buffer + * @return + */ +mapcache_image* _mapcache_imageio_gif_decode(mapcache_context *ctx, mapcache_buffer *buffer); + +/** + * @param r + * @param buffer + * @return + */ +void _mapcache_imageio_gif_decode_to_image(mapcache_context *ctx, mapcache_buffer *buffer, + mapcache_image *image); + +/** @} */ + +#endif // USE_GIF + typedef struct { double start; double end; diff --git a/lib/imageio.c b/lib/imageio.c index 2fe82b73..d0ff6c0b 100644 --- a/lib/imageio.c +++ b/lib/imageio.c @@ -37,7 +37,11 @@ int mapcache_imageio_is_valid_format(mapcache_context *ctx, mapcache_buffer *buffer) { mapcache_image_format_type t = mapcache_imageio_header_sniff(ctx,buffer); - if(t==GC_PNG || t==GC_JPEG) { + if(t==GC_PNG || t==GC_JPEG +#ifdef USE_GIF + || t==GC_GIF +#endif // USE_GIF + ) { return MAPCACHE_TRUE; } else { return MAPCACHE_FALSE; @@ -53,6 +57,10 @@ mapcache_image_format_type mapcache_imageio_header_sniff(mapcache_context *ctx, return GC_PNG; } else if(buffer->size >= 2 && ((unsigned char*)buffer->buf)[0] == 0xFF && ((unsigned char*)buffer->buf)[1] == 0xD8) { return GC_JPEG; +#ifdef USE_GIF + } else if(buffer->size >= 3 && ((char*)buffer->buf)[0] == 'G' && ((char*)buffer->buf)[1] == 'I' && ((char*)buffer->buf)[2] == 'F') { + return GC_GIF; +#endif // USE_GIF } else { return GC_UNKNOWN; } @@ -67,6 +75,10 @@ mapcache_image* mapcache_imageio_decode(mapcache_context *ctx, mapcache_buffer * return _mapcache_imageio_png_decode(ctx,buffer); } else if(type == GC_JPEG) { return _mapcache_imageio_jpeg_decode(ctx,buffer); +#ifdef USE_GIF + } else if(type == GC_GIF) { + return _mapcache_imageio_gif_decode(ctx,buffer); +#endif // USE_GIF } else { ctx->set_error(ctx, 500, "mapcache_imageio_decode: unrecognized image format"); return NULL; @@ -95,6 +107,10 @@ void mapcache_imageio_decode_to_image(mapcache_context *ctx, mapcache_buffer *bu _mapcache_imageio_png_decode_to_image(ctx,buffer,image); } else if(type == GC_JPEG) { _mapcache_imageio_jpeg_decode_to_image(ctx,buffer,image); +#ifdef USE_GIF + } else if(type == GC_GIF) { + _mapcache_imageio_gif_decode_to_image(ctx,buffer,image); +#endif } else { ctx->set_error(ctx, 500, "mapcache_imageio_decode: unrecognized image format"); } diff --git a/lib/imageio_gif.c b/lib/imageio_gif.c new file mode 100644 index 00000000..02839ec5 --- /dev/null +++ b/lib/imageio_gif.c @@ -0,0 +1,181 @@ +/****************************************************************************** + * $Id$ + * + * Project: MapServer + * Purpose: MapCache tile caching support file: GIF format + * Author: Thomas Bonfort and the MapServer team. + * + ****************************************************************************** + * Copyright (c) 1996-2011 Regents of the University of Minnesota. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies of this Software or works derived from this Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#include "mapcache-config.h" +#ifdef USE_GIF + +#include "mapcache.h" +#include +#include + +#ifdef _WIN32 +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long int uint64_t; +#endif + +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif + +/**\addtogroup imageio_gif */ +/** @{ */ +typedef struct _mapcache_buffer_closure _mapcache_buffer_closure; +struct _mapcache_buffer_closure{ + mapcache_buffer *buffer; + mapcache_image *image; + char * ptr; + size_t remaining; +}; + +static int _mapcache_imageio_gif_read_func(GifFileType * gif, GifByteType * buff, int length) +{ + _mapcache_buffer_closure *bc = (_mapcache_buffer_closure*)gif->UserData; + length = MIN(length, bc->remaining); + memcpy(buff, bc->ptr, length); + bc->remaining -= length; + bc->ptr += length; + return length; +} + +void _mapcache_imageio_gif_decode_to_image(mapcache_context *ctx, mapcache_buffer *buffer, + mapcache_image *img) +{ +static const unsigned int InterlacedOffset[] = { 0, 4, 2, 1 }; /* The way Interlaced image should. */ +static const unsigned int InterlacedJumps[] = { 8, 8, 4, 2 }; /* be read - offsets and jumps... */ + + _mapcache_buffer_closure bc; + GifFileType * gif; + GifRecordType record_type; + int ExtCode; + GifByteType *Extension, *c, *rgba; + static GifColorType *ColorMapEntry; + static ColorMapObject *ColorMap; + int Row, Col, Width, Height, i,j,n, transparent; + + bc.ptr = buffer->buf; + bc.remaining = buffer->size; + if((gif = DGifOpen(&bc, _mapcache_imageio_gif_read_func)) == NULL) + goto _error; + + img->w = gif->SWidth; + img->h = gif->SHeight; + transparent = -1; + + if(!img->data) { + img->data = calloc(1, img->w * img->h * 4 * sizeof(unsigned char)); + apr_pool_cleanup_register(ctx->pool, img->data, (void*)free, apr_pool_cleanup_null) ; + img->stride = img->w * 4; + } + +_read_record: + if(DGifGetRecordType(gif, &record_type) == GIF_ERROR) + goto _error; + + if(record_type == IMAGE_DESC_RECORD_TYPE) + goto _read_desc; + if(record_type == EXTENSION_RECORD_TYPE) + goto _read_ext; + if(record_type == TERMINATE_RECORD_TYPE) + goto _out; + goto _read_record; + +_read_desc: + if (DGifGetImageDesc(gif) == GIF_ERROR) + goto _error; + { + Row = gif->Image.Top; /* Image Position relative to Screen. */ + Col = gif->Image.Left; + Width = gif->Image.Width; + Height = gif->Image.Height; + ColorMap = gif->Image.ColorMap? gif->Image.ColorMap: gif->SColorMap; + if(Col + Width > gif->SWidth || Row + Height > gif->SHeight || !ColorMap) + goto _error; + if(gif->Image.Interlace) { + /* Need to perform 4 passes on the images: */ + for (i = 0; i < 4; i++) + for (j = Row + InterlacedOffset[i]; j < Row + Height; + j += InterlacedJumps[i]) + { + if (DGifGetLine(gif, &(img->data[j * img->stride + Col]), Width) == GIF_ERROR) + goto _error; + } + } else { + for (i = Row; i < Row + Height; i++) { + if(DGifGetLine(gif, &img->data[i * img->stride + Col], + Width) == GIF_ERROR) + goto _error; + } + } + } + for (i = Row; i < Row + Height; i++) { + c = &(img->data[i * img->stride + Col + Width -1]); + rgba = &(img->data[i * img->stride + img->stride -1]); + for(n = Width; n > 0; n--, c--) { + ColorMapEntry = &ColorMap->Colors[*c]; + *rgba-- = (*c == transparent)? 0: 255; + *rgba-- = ColorMapEntry->Red; + *rgba-- = ColorMapEntry->Green; + *rgba-- = ColorMapEntry->Blue; + } + } + goto _out; + +_read_ext: + /* Skip any extension blocks in file: */ + if (DGifGetExtension(gif, &ExtCode, &Extension) == GIF_ERROR) + goto _error; + if(ExtCode == 0xf9 && (Extension[1] & 0x01)) + transparent = Extension[4]; + while (Extension != NULL) { + if (DGifGetExtensionNext(gif, &Extension) == GIF_ERROR) + goto _error; + } + goto _read_record; + +_error: +_out: + DGifCloseFile(gif); +} + +mapcache_image* _mapcache_imageio_gif_decode(mapcache_context *ctx, mapcache_buffer *buffer) +{ + mapcache_image *img = mapcache_image_create(ctx); + _mapcache_imageio_gif_decode_to_image(ctx,buffer,img); + if(GC_HAS_ERROR(ctx)) + return NULL; + return img; +} + +/** @} */ + +/* vim: ai ts=3 sts=3 et sw=3 +*/ +#endif /* USE_GIF */