Pillow/src/libImaging/Storage.c

588 lines
16 KiB
C
Raw Normal View History

2010-07-31 06:52:47 +04:00
/*
* 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
2010-07-31 06:52:47 +04:00
* 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
2010-07-31 06:52:47 +04:00
* Copyright (c) 1995-2005 by Fredrik Lundh
*
* See the README file for information on usage and redistribution.
*/
#include "Imaging.h"
2013-09-30 12:04:58 +04:00
#include <string.h>
2010-07-31 06:52:47 +04:00
int ImagingNewCount = 0;
/* --------------------------------------------------------------------
* Standard image object.
*/
Imaging
2021-01-03 06:17:51 +03:00
ImagingNewPrologueSubtype(const char *mode, int xsize, int ysize, int size) {
2010-07-31 06:52:47 +04:00
Imaging im;
2016-05-21 17:23:54 +03:00
/* linesize overflow check, roughly the current largest space req'd */
if (xsize > (INT_MAX / 4) - 1) {
2021-01-03 06:17:51 +03:00
return (Imaging)ImagingError_MemoryError();
2016-05-21 17:23:54 +03:00
}
2021-01-03 06:17:51 +03:00
im = (Imaging)calloc(1, size);
2017-08-05 18:59:16 +03:00
if (!im) {
2021-01-03 06:17:51 +03:00
return (Imaging)ImagingError_MemoryError();
2017-08-05 18:59:16 +03:00
}
2010-07-31 06:52:47 +04:00
/* 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;
2016-05-10 21:46:40 +03:00
} 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;
2010-07-31 06:52:47 +04:00
} 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;
2021-01-03 06:17:51 +03:00
} else if (
strcmp(mode, "I;16") == 0 || strcmp(mode, "I;16L") == 0 ||
strcmp(mode, "I;16B") == 0 || strcmp(mode, "I;16N") == 0) {
2010-07-31 06:52:47 +04:00
/* 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 */
2019-09-05 13:11:02 +03:00
/* 15-bit reversed true colour */
2010-07-31 06:52:47 +04:00
im->bands = 1;
im->pixelsize = 2;
2021-01-03 06:17:51 +03:00
im->linesize = (xsize * 2 + 3) & -4;
2010-07-31 06:52:47 +04:00
im->type = IMAGING_TYPE_SPECIAL;
} else if (strcmp(mode, "BGR;16") == 0) {
/* EXPERIMENTAL */
/* 16-bit reversed true colour */
im->bands = 1;
im->pixelsize = 2;
2021-01-03 06:17:51 +03:00
im->linesize = (xsize * 2 + 3) & -4;
2010-07-31 06:52:47 +04:00
im->type = IMAGING_TYPE_SPECIAL;
} else if (strcmp(mode, "BGR;24") == 0) {
/* EXPERIMENTAL */
/* 24-bit reversed true colour */
im->bands = 1;
im->pixelsize = 3;
2021-01-03 06:17:51 +03:00
im->linesize = (xsize * 3 + 3) & -4;
2010-07-31 06:52:47 +04:00
im->type = IMAGING_TYPE_SPECIAL;
} else if (strcmp(mode, "BGR;32") == 0) {
/* EXPERIMENTAL */
/* 32-bit reversed true colour */
im->bands = 1;
im->pixelsize = 4;
2021-01-03 06:17:51 +03:00
im->linesize = (xsize * 4 + 3) & -4;
2010-07-31 06:52:47 +04:00
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;
2013-10-11 10:27:34 +04:00
} else if (strcmp(mode, "LAB") == 0) {
/* 24-bit color, luminance, + 2 color channels */
2013-10-12 10:31:26 +04:00
/* L is uint8, a,b are int8 */
2013-10-11 10:27:34 +04:00
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;
2013-10-11 10:27:34 +04:00
2010-07-31 06:52:47 +04:00
} else {
free(im);
2021-01-03 06:17:51 +03:00
return (Imaging)ImagingError_ValueError("unrecognized image mode");
2010-07-31 06:52:47 +04:00
}
/* 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) */
2021-01-03 06:17:51 +03:00
im->image = (char **)calloc((ysize > 0) ? ysize : 1, sizeof(void *));
2010-07-31 06:52:47 +04:00
2021-01-03 06:17:51 +03:00
if (!im->image) {
free(im);
2021-01-03 06:17:51 +03:00
return (Imaging)ImagingError_MemoryError();
2010-07-31 06:52:47 +04:00
}
/* Initialize alias pointers to pixel data. */
switch (im->pixelsize) {
2021-01-03 06:17:51 +03:00
case 1:
case 2:
case 3:
im->image8 = (UINT8 **)im->image;
break;
case 4:
im->image32 = (INT32 **)im->image;
break;
}
2017-09-17 21:31:50 +03:00
ImagingDefaultArena.stats_new_count += 1;
2010-07-31 06:52:47 +04:00
return im;
}
Imaging
2021-01-03 06:17:51 +03:00
ImagingNewPrologue(const char *mode, int xsize, int ysize) {
2010-07-31 06:52:47 +04:00
return ImagingNewPrologueSubtype(
2017-08-05 18:59:16 +03:00
mode, xsize, ysize, sizeof(struct ImagingMemoryInstance));
2010-07-31 06:52:47 +04:00
}
void
2021-01-03 06:17:51 +03:00
ImagingDelete(Imaging im) {
2020-05-10 12:56:36 +03:00
if (!im) {
return;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
2020-05-10 12:56:36 +03:00
if (im->palette) {
ImagingPaletteDelete(im->palette);
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
2020-05-10 12:56:36 +03:00
if (im->destroy) {
im->destroy(im);
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
2020-05-10 12:56:36 +03:00
if (im->image) {
free(im->image);
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
free(im);
}
/* Array Storage Type */
/* ------------------ */
/* Allocate image as an array of line buffers. */
#define IMAGING_PAGE_SIZE (4096)
2017-09-17 21:31:50 +03:00
struct ImagingMemoryArena ImagingDefaultArena = {
2021-01-03 06:17:51 +03:00
1, // alignment
16 * 1024 * 1024, // block_size
0, // blocks_max
0, // blocks_cached
NULL, // blocks_pool
0,
0,
0,
0,
0 // Stats
2017-09-17 20:31:13 +03:00
};
2017-09-17 20:10:31 +03:00
2017-09-18 01:41:39 +03:00
int
2021-01-03 06:17:51 +03:00
ImagingMemorySetBlocksMax(ImagingMemoryArena arena, int blocks_max) {
2017-09-18 01:41:39 +03:00
void *p;
2017-09-17 20:31:13 +03:00
/* Free already cached blocks */
2017-09-18 03:57:43 +03:00
ImagingMemoryClearCache(arena, blocks_max);
2017-09-17 21:31:50 +03:00
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);
2021-01-03 06:17:51 +03:00
if (!p) {
2017-09-18 01:41:39 +03:00
// Leave previous blocks_max value
return 0;
}
arena->blocks_pool = p;
2017-09-17 21:31:50 +03:00
} else {
arena->blocks_pool = calloc(sizeof(*arena->blocks_pool), blocks_max);
2021-01-03 06:17:51 +03:00
if (!arena->blocks_pool) {
2017-09-18 01:41:39 +03:00
return 0;
}
}
arena->blocks_max = blocks_max;
return 1;
}
void
2021-01-03 06:17:51 +03:00
ImagingMemoryClearCache(ImagingMemoryArena arena, int new_size) {
2017-09-18 03:57:43 +03:00
while (arena->blocks_cached > new_size) {
2017-09-18 02:37:47 +03:00
arena->blocks_cached -= 1;
free(arena->blocks_pool[arena->blocks_cached].ptr);
2017-09-18 01:41:39 +03:00
arena->stats_freed_blocks += 1;
2017-09-17 21:31:50 +03:00
}
2017-09-17 20:10:31 +03:00
}
2017-09-18 22:41:28 +03:00
ImagingMemoryBlock
2021-01-03 06:17:51 +03:00
memory_get_block(ImagingMemoryArena arena, int requested_size, int dirty) {
2017-09-18 22:41:28 +03:00
ImagingMemoryBlock block = {NULL, 0};
2017-09-18 02:37:47 +03:00
if (arena->blocks_cached > 0) {
2017-09-18 22:41:28 +03:00
// Get block from cache
2017-09-18 02:37:47 +03:00
arena->blocks_cached -= 1;
block = arena->blocks_pool[arena->blocks_cached];
2017-09-18 22:41:28 +03:00
// Reallocate if needed
2021-01-03 06:17:51 +03:00
if (block.size != requested_size) {
2017-09-18 22:41:28 +03:00
block.ptr = realloc(block.ptr, requested_size);
}
2021-01-03 06:17:51 +03:00
if (!block.ptr) {
2018-01-14 04:09:05 +03:00
// Can't allocate, free previous pointer (it is still valid)
free(arena->blocks_pool[arena->blocks_cached].ptr);
2017-09-18 01:41:39 +03:00
arena->stats_freed_blocks += 1;
2017-09-18 22:41:28 +03:00
return block;
}
2021-01-03 06:17:51 +03:00
if (!dirty) {
2017-09-18 22:41:28 +03:00
memset(block.ptr, 0, requested_size);
}
2017-09-18 01:41:39 +03:00
arena->stats_reused_blocks += 1;
if (block.ptr != arena->blocks_pool[arena->blocks_cached].ptr) {
2017-09-18 01:41:39 +03:00
arena->stats_reallocated_blocks += 1;
}
} else {
if (dirty) {
2017-09-18 22:41:28 +03:00
block.ptr = malloc(requested_size);
} else {
2017-09-18 22:41:28 +03:00
block.ptr = calloc(1, requested_size);
}
2017-09-18 01:41:39 +03:00
arena->stats_allocated_blocks += 1;
}
2017-09-18 22:41:28 +03:00
block.size = requested_size;
return block;
}
void
2021-01-03 06:17:51 +03:00
memory_return_block(ImagingMemoryArena arena, ImagingMemoryBlock block) {
if (arena->blocks_cached < arena->blocks_max) {
2017-09-19 00:14:44 +03:00
// 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;
2017-09-18 02:37:47 +03:00
arena->blocks_cached += 1;
2017-09-15 18:00:15 +03:00
} else {
2017-09-18 22:41:28 +03:00
free(block.ptr);
2017-09-18 01:41:39 +03:00
arena->stats_freed_blocks += 1;
2017-09-15 18:00:15 +03:00
}
}
2010-07-31 06:52:47 +04:00
static void
2021-01-03 06:17:51 +03:00
ImagingDestroyArray(Imaging im) {
int y = 0;
2010-07-31 06:52:47 +04:00
if (im->blocks) {
2017-09-18 22:41:28 +03:00
while (im->blocks[y].ptr) {
2021-01-03 06:17:51 +03:00
memory_return_block(&ImagingDefaultArena, im->blocks[y]);
2017-09-15 18:00:15 +03:00
y += 1;
}
free(im->blocks);
}
2010-07-31 06:52:47 +04:00
}
Imaging
2021-01-03 06:17:51 +03:00
ImagingAllocateArray(Imaging im, int dirty, int block_size) {
int y, line_in_block, current_block;
2017-09-17 21:31:50 +03:00
ImagingMemoryArena arena = &ImagingDefaultArena;
2017-09-18 22:41:28 +03:00
ImagingMemoryBlock block = {NULL, 0};
2017-09-23 04:44:01 +03:00
int aligned_linesize, lines_per_block, blocks_count;
char *aligned_ptr = NULL;
/* 0-width or 0-height image. No need to do anything */
2021-01-03 06:17:51 +03:00
if (!im->linesize || !im->ysize) {
return im;
}
2017-09-23 04:44:01 +03:00
aligned_linesize = (im->linesize + arena->alignment - 1) & -arena->alignment;
lines_per_block = (block_size - (arena->alignment - 1)) / aligned_linesize;
2020-05-10 12:56:36 +03:00
if (lines_per_block == 0) {
lines_per_block = 1;
2020-05-10 12:56:36 +03:00
}
blocks_count = (im->ysize + lines_per_block - 1) / lines_per_block;
2017-09-17 01:40:30 +03:00
// printf("NEW size: %dx%d, ls: %d, lpb: %d, blocks: %d\n",
2017-09-23 04:44:01 +03:00
// im->xsize, im->ysize, aligned_linesize, lines_per_block, blocks_count);
2010-07-31 06:52:47 +04:00
2019-06-10 04:55:41 +03:00
/* One extra pointer is always NULL */
2017-09-18 22:41:28 +03:00
im->blocks = calloc(sizeof(*im->blocks), blocks_count + 1);
2021-01-03 06:17:51 +03:00
if (!im->blocks) {
return (Imaging)ImagingError_MemoryError();
}
2010-07-31 06:52:47 +04:00
/* 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;
2017-09-30 04:16:12 +03:00
int lines_remaining = lines_per_block;
if (lines_remaining > im->ysize - y) {
lines_remaining = im->ysize - y;
}
2017-09-30 04:16:12 +03:00
required = lines_remaining * aligned_linesize + arena->alignment - 1;
block = memory_get_block(arena, required, dirty);
2021-01-03 06:17:51 +03:00
if (!block.ptr) {
ImagingDestroyArray(im);
2021-01-03 06:17:51 +03:00
return (Imaging)ImagingError_MemoryError();
}
2017-09-18 22:41:28 +03:00
im->blocks[current_block] = block;
/* Bulletproof code from libc _int_memalign */
2017-09-23 04:56:18 +03:00
aligned_ptr = (char *)(
2017-10-02 06:07:00 +03:00
((size_t) (block.ptr + arena->alignment - 1)) &
2017-10-02 08:16:13 +03:00
-((Py_ssize_t) arena->alignment));
}
2017-09-23 04:44:01 +03:00
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;
}
2010-07-31 06:52:47 +04:00
}
im->destroy = ImagingDestroyArray;
return im;
2010-07-31 06:52:47 +04:00
}
/* Block Storage Type */
/* ------------------ */
/* Allocate image as a single block. */
static void
2021-01-03 06:17:51 +03:00
ImagingDestroyBlock(Imaging im) {
2020-05-10 12:56:36 +03:00
if (im->block) {
free(im->block);
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
}
Imaging
2021-01-03 06:17:51 +03:00
ImagingAllocateBlock(Imaging im) {
Py_ssize_t y, i;
2010-07-31 06:52:47 +04:00
2017-09-15 18:11:20 +03:00
/* overflow check for malloc */
2021-01-03 06:17:51 +03:00
if (im->linesize && im->ysize > INT_MAX / im->linesize) {
return (Imaging)ImagingError_MemoryError();
2016-03-16 14:47:18 +03:00
}
2016-05-21 17:24:20 +03:00
if (im->ysize * im->linesize <= 0) {
2010-07-31 06:52:47 +04:00
/* some platforms return NULL for malloc(0); this fix
prevents MemoryError on zero-sized images on such
platforms */
2021-01-03 06:17:51 +03:00
im->block = (char *)malloc(1);
2016-05-21 17:24:20 +03:00
} else {
2017-09-15 18:11:20 +03:00
/* malloc check ok, overflow check above */
2021-01-03 06:17:51 +03:00
im->block = (char *)calloc(im->ysize, im->linesize);
2016-05-21 17:24:20 +03:00
}
2010-07-31 06:52:47 +04:00
2021-01-03 06:17:51 +03:00
if (!im->block) {
return (Imaging)ImagingError_MemoryError();
}
2010-07-31 06:52:47 +04:00
for (y = i = 0; y < im->ysize; y++) {
im->image[y] = im->block + i;
i += im->linesize;
2010-07-31 06:52:47 +04:00
}
2018-01-14 04:09:05 +03:00
im->destroy = ImagingDestroyBlock;
2010-07-31 06:52:47 +04:00
return im;
2010-07-31 06:52:47 +04:00
}
/* --------------------------------------------------------------------
* Create a new, internally allocated, image.
*/
Imaging
2021-01-03 06:17:51 +03:00
ImagingNewInternal(const char *mode, int xsize, int ysize, int dirty) {
2010-07-31 06:52:47 +04:00
Imaging im;
if (xsize < 0 || ysize < 0) {
2021-01-03 06:17:51 +03:00
return (Imaging)ImagingError_ValueError("bad image size");
}
im = ImagingNewPrologue(mode, xsize, ysize);
2021-01-03 06:17:51 +03:00
if (!im) {
return NULL;
2020-05-10 12:56:36 +03:00
}
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
2021-01-03 06:17:51 +03:00
ImagingNew(const char *mode, int xsize, int ysize) {
return ImagingNewInternal(mode, xsize, ysize, 0);
}
Imaging
2021-01-03 06:17:51 +03:00
ImagingNewDirty(const char *mode, int xsize, int ysize) {
return ImagingNewInternal(mode, xsize, ysize, 1);
}
Imaging
2021-01-03 06:17:51 +03:00
ImagingNewBlock(const char *mode, int xsize, int ysize) {
Imaging im;
2017-09-15 00:09:36 +03:00
if (xsize < 0 || ysize < 0) {
2021-01-03 06:17:51 +03:00
return (Imaging)ImagingError_ValueError("bad image size");
2017-09-15 00:09:36 +03:00
}
im = ImagingNewPrologue(mode, xsize, ysize);
2021-01-03 06:17:51 +03:00
if (!im) {
return NULL;
2020-05-10 12:56:36 +03:00
}
2017-09-15 18:11:20 +03:00
if (ImagingAllocateBlock(im)) {
return im;
}
ImagingDelete(im);
return NULL;
2010-07-31 06:52:47 +04:00
}
Imaging
2021-01-03 06:17:51 +03:00
ImagingNew2Dirty(const char *mode, Imaging imOut, Imaging imIn) {
2010-07-31 06:52:47 +04:00
/* allocate or validate output image */
if (imOut) {
/* make sure images match */
2021-01-03 06:17:51 +03:00
if (strcmp(imOut->mode, mode) != 0 || imOut->xsize != imIn->xsize ||
imOut->ysize != imIn->ysize) {
2010-07-31 06:52:47 +04:00
return ImagingError_Mismatch();
}
} else {
/* create new image */
2017-08-06 20:08:07 +03:00
imOut = ImagingNewDirty(mode, imIn->xsize, imIn->ysize);
2020-05-10 12:56:36 +03:00
if (!imOut) {
2010-07-31 06:52:47 +04:00
return NULL;
2020-05-10 12:56:36 +03:00
}
2010-07-31 06:52:47 +04:00
}
return imOut;
}
void
2021-01-03 06:17:51 +03:00
ImagingCopyPalette(Imaging destination, Imaging source) {
2010-07-31 06:52:47 +04:00
if (source->palette) {
2020-05-10 12:56:36 +03:00
if (destination->palette) {
2010-07-31 06:52:47 +04:00
ImagingPaletteDelete(destination->palette);
2020-05-10 12:56:36 +03:00
}
destination->palette = ImagingPaletteDuplicate(source->palette);
2010-07-31 06:52:47 +04:00
}
}