123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594 |
- /*
- * The Python Imaging Library
- * $Id$
- *
- * imaging storage object
- *
- * This baseline implementation is designed to efficiently handle
- * large images, provided they fit into the available memory.
- *
- * history:
- * 1995-06-15 fl Created
- * 1995-09-12 fl Updated API, compiles silently under ANSI C++
- * 1995-11-26 fl Compiles silently under Borland 4.5 as well
- * 1996-05-05 fl Correctly test status from Prologue
- * 1997-05-12 fl Increased THRESHOLD (to speed up Tk interface)
- * 1997-05-30 fl Added support for floating point images
- * 1997-11-17 fl Added support for "RGBX" images
- * 1998-01-11 fl Added support for integer images
- * 1998-03-05 fl Exported Prologue/Epilogue functions
- * 1998-07-01 fl Added basic "YCrCb" support
- * 1998-07-03 fl Attach palette in prologue for "P" images
- * 1998-07-09 hk Don't report MemoryError on zero-size images
- * 1998-07-12 fl Change "YCrCb" to "YCbCr" (!)
- * 1998-10-26 fl Added "I;16" and "I;16B" storage modes (experimental)
- * 1998-12-29 fl Fixed allocation bug caused by previous fix
- * 1999-02-03 fl Added "RGBa" and "BGR" modes (experimental)
- * 2001-04-22 fl Fixed potential memory leak in ImagingCopyPalette
- * 2003-09-26 fl Added "LA" and "PA" modes (experimental)
- * 2005-10-02 fl Added image counter
- *
- * Copyright (c) 1998-2005 by Secret Labs AB
- * Copyright (c) 1995-2005 by Fredrik Lundh
- *
- * See the README file for information on usage and redistribution.
- */
- #include "Imaging.h"
- #include <string.h>
- int ImagingNewCount = 0;
- /* --------------------------------------------------------------------
- * Standard image object.
- */
- Imaging
- ImagingNewPrologueSubtype(const char *mode, int xsize, int ysize, int size)
- {
- Imaging im;
- /* linesize overflow check, roughly the current largest space req'd */
- if (xsize > (INT_MAX / 4) - 1) {
- return (Imaging) ImagingError_MemoryError();
- }
- im = (Imaging) calloc(1, size);
- if (!im) {
- return (Imaging) ImagingError_MemoryError();
- }
- /* Setup image descriptor */
- im->xsize = xsize;
- im->ysize = ysize;
- im->type = IMAGING_TYPE_UINT8;
- if (strcmp(mode, "1") == 0) {
- /* 1-bit images */
- im->bands = im->pixelsize = 1;
- im->linesize = xsize;
- } else if (strcmp(mode, "P") == 0) {
- /* 8-bit palette mapped images */
- im->bands = im->pixelsize = 1;
- im->linesize = xsize;
- im->palette = ImagingPaletteNew("RGB");
- } else if (strcmp(mode, "PA") == 0) {
- /* 8-bit palette with alpha */
- im->bands = 2;
- im->pixelsize = 4; /* store in image32 memory */
- im->linesize = xsize * 4;
- im->palette = ImagingPaletteNew("RGB");
- } else if (strcmp(mode, "L") == 0) {
- /* 8-bit greyscale (luminance) images */
- im->bands = im->pixelsize = 1;
- im->linesize = xsize;
- } else if (strcmp(mode, "LA") == 0) {
- /* 8-bit greyscale (luminance) with alpha */
- im->bands = 2;
- im->pixelsize = 4; /* store in image32 memory */
- im->linesize = xsize * 4;
- } else if (strcmp(mode, "La") == 0) {
- /* 8-bit greyscale (luminance) with premultiplied alpha */
- im->bands = 2;
- im->pixelsize = 4; /* store in image32 memory */
- im->linesize = xsize * 4;
- } else if (strcmp(mode, "F") == 0) {
- /* 32-bit floating point images */
- im->bands = 1;
- im->pixelsize = 4;
- im->linesize = xsize * 4;
- im->type = IMAGING_TYPE_FLOAT32;
- } else if (strcmp(mode, "I") == 0) {
- /* 32-bit integer images */
- im->bands = 1;
- im->pixelsize = 4;
- im->linesize = xsize * 4;
- im->type = IMAGING_TYPE_INT32;
- } else if (strcmp(mode, "I;16") == 0 || strcmp(mode, "I;16L") == 0 \
- || strcmp(mode, "I;16B") == 0 || strcmp(mode, "I;16N") == 0) {
- /* EXPERIMENTAL */
- /* 16-bit raw integer images */
- im->bands = 1;
- im->pixelsize = 2;
- im->linesize = xsize * 2;
- im->type = IMAGING_TYPE_SPECIAL;
- } else if (strcmp(mode, "RGB") == 0) {
- /* 24-bit true colour images */
- im->bands = 3;
- im->pixelsize = 4;
- im->linesize = xsize * 4;
- } else if (strcmp(mode, "BGR;15") == 0) {
- /* EXPERIMENTAL */
- /* 15-bit reversed true colour */
- im->bands = 1;
- im->pixelsize = 2;
- im->linesize = (xsize*2 + 3) & -4;
- im->type = IMAGING_TYPE_SPECIAL;
- } else if (strcmp(mode, "BGR;16") == 0) {
- /* EXPERIMENTAL */
- /* 16-bit reversed true colour */
- im->bands = 1;
- im->pixelsize = 2;
- im->linesize = (xsize*2 + 3) & -4;
- im->type = IMAGING_TYPE_SPECIAL;
- } else if (strcmp(mode, "BGR;24") == 0) {
- /* EXPERIMENTAL */
- /* 24-bit reversed true colour */
- im->bands = 1;
- im->pixelsize = 3;
- im->linesize = (xsize*3 + 3) & -4;
- im->type = IMAGING_TYPE_SPECIAL;
- } else if (strcmp(mode, "BGR;32") == 0) {
- /* EXPERIMENTAL */
- /* 32-bit reversed true colour */
- im->bands = 1;
- im->pixelsize = 4;
- im->linesize = (xsize*4 + 3) & -4;
- im->type = IMAGING_TYPE_SPECIAL;
- } else if (strcmp(mode, "RGBX") == 0) {
- /* 32-bit true colour images with padding */
- im->bands = im->pixelsize = 4;
- im->linesize = xsize * 4;
- } else if (strcmp(mode, "RGBA") == 0) {
- /* 32-bit true colour images with alpha */
- im->bands = im->pixelsize = 4;
- im->linesize = xsize * 4;
- } else if (strcmp(mode, "RGBa") == 0) {
- /* 32-bit true colour images with premultiplied alpha */
- im->bands = im->pixelsize = 4;
- im->linesize = xsize * 4;
- } else if (strcmp(mode, "CMYK") == 0) {
- /* 32-bit colour separation */
- im->bands = im->pixelsize = 4;
- im->linesize = xsize * 4;
- } else if (strcmp(mode, "YCbCr") == 0) {
- /* 24-bit video format */
- im->bands = 3;
- im->pixelsize = 4;
- im->linesize = xsize * 4;
- } else if (strcmp(mode, "LAB") == 0) {
- /* 24-bit color, luminance, + 2 color channels */
- /* L is uint8, a,b are int8 */
- im->bands = 3;
- im->pixelsize = 4;
- im->linesize = xsize * 4;
- } else if (strcmp(mode, "HSV") == 0) {
- /* 24-bit color, luminance, + 2 color channels */
- /* L is uint8, a,b are int8 */
- im->bands = 3;
- im->pixelsize = 4;
- im->linesize = xsize * 4;
- } else {
- free(im);
- return (Imaging) ImagingError_ValueError("unrecognized image mode");
- }
- /* Setup image descriptor */
- strcpy(im->mode, mode);
- /* Pointer array (allocate at least one line, to avoid MemoryError
- exceptions on platforms where calloc(0, x) returns NULL) */
- im->image = (char **) calloc((ysize > 0) ? ysize : 1, sizeof(void *));
- if ( ! im->image) {
- free(im);
- return (Imaging) ImagingError_MemoryError();
- }
- /* Initialize alias pointers to pixel data. */
- switch (im->pixelsize) {
- case 1: case 2: case 3:
- im->image8 = (UINT8 **) im->image;
- break;
- case 4:
- im->image32 = (INT32 **) im->image;
- break;
- }
- ImagingDefaultArena.stats_new_count += 1;
- return im;
- }
- Imaging
- ImagingNewPrologue(const char *mode, int xsize, int ysize)
- {
- return ImagingNewPrologueSubtype(
- mode, xsize, ysize, sizeof(struct ImagingMemoryInstance));
- }
- void
- ImagingDelete(Imaging im)
- {
- if (!im)
- return;
- if (im->palette)
- ImagingPaletteDelete(im->palette);
- if (im->destroy)
- im->destroy(im);
- if (im->image)
- free(im->image);
- free(im);
- }
- /* Array Storage Type */
- /* ------------------ */
- /* Allocate image as an array of line buffers. */
- #define IMAGING_PAGE_SIZE (4096)
- struct ImagingMemoryArena ImagingDefaultArena = {
- 1, // alignment
- 16*1024*1024, // block_size
- 0, // blocks_max
- 0, // blocks_cached
- NULL, // blocks_pool
- 0, 0, 0, 0, 0 // Stats
- };
- int
- ImagingMemorySetBlocksMax(ImagingMemoryArena arena, int blocks_max)
- {
- void *p;
- /* Free already cached blocks */
- ImagingMemoryClearCache(arena, blocks_max);
- if (blocks_max == 0 && arena->blocks_pool != NULL) {
- free(arena->blocks_pool);
- arena->blocks_pool = NULL;
- } else if (arena->blocks_pool != NULL) {
- p = realloc(arena->blocks_pool, sizeof(*arena->blocks_pool) * blocks_max);
- if ( ! p) {
- // Leave previous blocks_max value
- return 0;
- }
- arena->blocks_pool = p;
- } else {
- arena->blocks_pool = calloc(sizeof(*arena->blocks_pool), blocks_max);
- if ( ! arena->blocks_pool) {
- return 0;
- }
- }
- arena->blocks_max = blocks_max;
- return 1;
- }
- void
- ImagingMemoryClearCache(ImagingMemoryArena arena, int new_size)
- {
- while (arena->blocks_cached > new_size) {
- arena->blocks_cached -= 1;
- free(arena->blocks_pool[arena->blocks_cached].ptr);
- arena->stats_freed_blocks += 1;
- }
- }
- ImagingMemoryBlock
- memory_get_block(ImagingMemoryArena arena, int requested_size, int dirty)
- {
- ImagingMemoryBlock block = {NULL, 0};
- if (arena->blocks_cached > 0) {
- // Get block from cache
- arena->blocks_cached -= 1;
- block = arena->blocks_pool[arena->blocks_cached];
- // Reallocate if needed
- if (block.size != requested_size){
- block.ptr = realloc(block.ptr, requested_size);
- }
- if ( ! block.ptr) {
- // Can't allocate, free previous pointer (it is still valid)
- free(arena->blocks_pool[arena->blocks_cached].ptr);
- arena->stats_freed_blocks += 1;
- return block;
- }
- if ( ! dirty) {
- memset(block.ptr, 0, requested_size);
- }
- arena->stats_reused_blocks += 1;
- if (block.ptr != arena->blocks_pool[arena->blocks_cached].ptr) {
- arena->stats_reallocated_blocks += 1;
- }
- } else {
- if (dirty) {
- block.ptr = malloc(requested_size);
- } else {
- block.ptr = calloc(1, requested_size);
- }
- arena->stats_allocated_blocks += 1;
- }
- block.size = requested_size;
- return block;
- }
- void
- memory_return_block(ImagingMemoryArena arena, ImagingMemoryBlock block)
- {
- if (arena->blocks_cached < arena->blocks_max) {
- // Reduce block size
- if (block.size > arena->block_size) {
- block.size = arena->block_size;
- block.ptr = realloc(block.ptr, arena->block_size);
- }
- arena->blocks_pool[arena->blocks_cached] = block;
- arena->blocks_cached += 1;
- } else {
- free(block.ptr);
- arena->stats_freed_blocks += 1;
- }
- }
- static void
- ImagingDestroyArray(Imaging im)
- {
- int y = 0;
- if (im->blocks) {
- while (im->blocks[y].ptr) {
- memory_return_block(&ImagingDefaultArena, im->blocks[y]);
- y += 1;
- }
- free(im->blocks);
- }
- }
- Imaging
- ImagingAllocateArray(Imaging im, int dirty, int block_size)
- {
- int y, line_in_block, current_block;
- ImagingMemoryArena arena = &ImagingDefaultArena;
- ImagingMemoryBlock block = {NULL, 0};
- int aligned_linesize, lines_per_block, blocks_count;
- char *aligned_ptr = NULL;
- /* 0-width or 0-height image. No need to do anything */
- if ( ! im->linesize || ! im->ysize) {
- return im;
- }
- aligned_linesize = (im->linesize + arena->alignment - 1) & -arena->alignment;
- lines_per_block = (block_size - (arena->alignment - 1)) / aligned_linesize;
- if (lines_per_block == 0)
- lines_per_block = 1;
- blocks_count = (im->ysize + lines_per_block - 1) / lines_per_block;
- // printf("NEW size: %dx%d, ls: %d, lpb: %d, blocks: %d\n",
- // im->xsize, im->ysize, aligned_linesize, lines_per_block, blocks_count);
- /* One extra pointer is always NULL */
- im->blocks = calloc(sizeof(*im->blocks), blocks_count + 1);
- if ( ! im->blocks) {
- return (Imaging) ImagingError_MemoryError();
- }
- /* Allocate image as an array of lines */
- line_in_block = 0;
- current_block = 0;
- for (y = 0; y < im->ysize; y++) {
- if (line_in_block == 0) {
- int required;
- int lines_remaining = lines_per_block;
- if (lines_remaining > im->ysize - y) {
- lines_remaining = im->ysize - y;
- }
- required = lines_remaining * aligned_linesize + arena->alignment - 1;
- block = memory_get_block(arena, required, dirty);
- if ( ! block.ptr) {
- ImagingDestroyArray(im);
- return (Imaging) ImagingError_MemoryError();
- }
- im->blocks[current_block] = block;
- /* Bulletproof code from libc _int_memalign */
- aligned_ptr = (char *)(
- ((size_t) (block.ptr + arena->alignment - 1)) &
- -((Py_ssize_t) arena->alignment));
- }
- im->image[y] = aligned_ptr + aligned_linesize * line_in_block;
- line_in_block += 1;
- if (line_in_block >= lines_per_block) {
- /* Reset counter and start new block */
- line_in_block = 0;
- current_block += 1;
- }
- }
- im->destroy = ImagingDestroyArray;
- return im;
- }
- /* Block Storage Type */
- /* ------------------ */
- /* Allocate image as a single block. */
- static void
- ImagingDestroyBlock(Imaging im)
- {
- if (im->block)
- free(im->block);
- }
- Imaging
- ImagingAllocateBlock(Imaging im)
- {
- Py_ssize_t y, i;
- /* overflow check for malloc */
- if (im->linesize &&
- im->ysize > INT_MAX / im->linesize) {
- return (Imaging) ImagingError_MemoryError();
- }
- if (im->ysize * im->linesize <= 0) {
- /* some platforms return NULL for malloc(0); this fix
- prevents MemoryError on zero-sized images on such
- platforms */
- im->block = (char *) malloc(1);
- } else {
- /* malloc check ok, overflow check above */
- im->block = (char *) calloc(im->ysize, im->linesize);
- }
- if ( ! im->block) {
- return (Imaging) ImagingError_MemoryError();
- }
- for (y = i = 0; y < im->ysize; y++) {
- im->image[y] = im->block + i;
- i += im->linesize;
- }
- im->destroy = ImagingDestroyBlock;
- return im;
- }
- /* --------------------------------------------------------------------
- * Create a new, internally allocated, image.
- */
- Imaging
- ImagingNewInternal(const char* mode, int xsize, int ysize, int dirty)
- {
- Imaging im;
- if (xsize < 0 || ysize < 0) {
- return (Imaging) ImagingError_ValueError("bad image size");
- }
- im = ImagingNewPrologue(mode, xsize, ysize);
- if ( ! im)
- return NULL;
- if (ImagingAllocateArray(im, dirty, ImagingDefaultArena.block_size)) {
- return im;
- }
- ImagingError_Clear();
- // Try to allocate the image once more with smallest possible block size
- if (ImagingAllocateArray(im, dirty, IMAGING_PAGE_SIZE)) {
- return im;
- }
- ImagingDelete(im);
- return NULL;
- }
- Imaging
- ImagingNew(const char* mode, int xsize, int ysize)
- {
- return ImagingNewInternal(mode, xsize, ysize, 0);
- }
- Imaging
- ImagingNewDirty(const char* mode, int xsize, int ysize)
- {
- return ImagingNewInternal(mode, xsize, ysize, 1);
- }
- Imaging
- ImagingNewBlock(const char* mode, int xsize, int ysize)
- {
- Imaging im;
- if (xsize < 0 || ysize < 0) {
- return (Imaging) ImagingError_ValueError("bad image size");
- }
- im = ImagingNewPrologue(mode, xsize, ysize);
- if ( ! im)
- return NULL;
- if (ImagingAllocateBlock(im)) {
- return im;
- }
- ImagingDelete(im);
- return NULL;
- }
- Imaging
- ImagingNew2Dirty(const char* mode, Imaging imOut, Imaging imIn)
- {
- /* allocate or validate output image */
- if (imOut) {
- /* make sure images match */
- if (strcmp(imOut->mode, mode) != 0
- || imOut->xsize != imIn->xsize
- || imOut->ysize != imIn->ysize) {
- return ImagingError_Mismatch();
- }
- } else {
- /* create new image */
- imOut = ImagingNewDirty(mode, imIn->xsize, imIn->ysize);
- if (!imOut)
- return NULL;
- }
- return imOut;
- }
- void
- ImagingCopyPalette(Imaging destination, Imaging source)
- {
- if (source->palette) {
- if (destination->palette)
- ImagingPaletteDelete(destination->palette);
- destination->palette = ImagingPaletteDuplicate(source->palette);
- }
- }
|