#include "imgutil.inl"
#include "color.h"
#include "palette.h"
#include "pixbuf.h"
#include <core/buffer.h>
#include <core/strings.h>
#include <core/stream.h>
#include <sewer/bmem.h>
#include <sewer/cassert.h>
typedef struct _gif_desc_t GifDesc;
typedef struct _gif_image_t GifImage;
struct _gif_desc_t
{
uint16_t width;
uint16_t height;
byte_t flags;
byte_t bgcolor;
byte_t aspect;
};
struct _gif_image_t
{
uint16_t left;
uint16_t top;
uint16_t width;
uint16_t height;
byte_t flags;
};
static bool_t i_parse_png(Stream *stm_in, Stream *stm_out)
{
endian_t rendian = stm_get_read_endian(stm_in);
endian_t wendian = ekBIGEND;
stm_set_read_endian(stm_in, ekBIGEND);
if (stm_out)
{
wendian = stm_get_write_endian(stm_out);
stm_set_write_endian(stm_out, ekBIGEND);
}
for (;;)
{
uint32_t size;
byte_t type[4];
uint32_t crc;
size = stm_read_u32(stm_in);
stm_read(stm_in, type, 4);
if (stm_out)
{
stm_write_u32(stm_out, size);
stm_write(stm_out, type, 4);
}
if (size > 0)
{
if (stm_out)
stm_pipe(stm_in, stm_out, size);
else
stm_skip(stm_in, size);
}
crc = stm_read_u32(stm_in);
if (stm_out)
stm_write_u32(stm_out, crc);
if (str_equ_cn(cast_const(type, char_t), "IEND", 4) == TRUE)
break;
}
stm_set_read_endian(stm_in, rendian);
if (stm_out)
stm_set_write_endian(stm_out, wendian);
return TRUE;
}
static void i_read_compressed(Stream *stm_in, byte_t *marker)
{
for (;;)
{
byte_t data[2];
stm_read(stm_in, data, 1);
if (data[0] == 0xFF)
{
stm_read(stm_in, data + 1, 1);
if (data[1] == 0x00 || (data[1] >= 0xd0 && data[1] <= 0xd7))
{
}
else
{
marker[0] = data[0];
marker[1] = data[1];
return;
}
}
}
}
static void i_read_compressed2(Stream *stm_in, Stream *stm_out, byte_t *marker)
{
for (;;)
{
byte_t data[2];
stm_read(stm_in, data, 1);
if (data[0] == 0xFF)
{
stm_read(stm_in, data + 1, 1);
if (data[1] == 0x00 || (data[1] >= 0xd0 && data[1] <= 0xd7))
{
stm_write(stm_out, data, 2);
}
else
{
marker[0] = data[0];
marker[1] = data[1];
return;
}
}
else
{
stm_write(stm_out, data, 1);
}
}
}
static bool_t i_parse_jpg(Stream *stm_in, Stream *stm_out)
{
endian_t rendian = stm_get_read_endian(stm_in);
endian_t wendian = ekBIGEND;
byte_t marker[2];
bool_t ok = TRUE;
stm_set_read_endian(stm_in, ekBIGEND);
if (stm_out)
{
wendian = stm_get_write_endian(stm_out);
stm_set_write_endian(stm_out, ekBIGEND);
}
stm_read(stm_in, marker, 2);
for (;;)
{
if (stm_out)
stm_write(stm_out, marker, 2);
if (marker[0] != 0xFF)
{
ok = FALSE;
break;
}
if (marker[1] == 0x01 || (marker[1] >= 0xD0 && marker[1] <= 0xD9))
{
if (marker[1] == 0xD9)
break;
stm_read(stm_in, marker, 2);
}
else
{
uint16_t size = stm_read_u16(stm_in);
if (stm_out)
stm_write_u16(stm_out, size);
if (size < 2)
{
ok = FALSE;
break;
}
size -= 2;
if (size > 0)
{
if (stm_out)
stm_pipe(stm_in, stm_out, size);
else
stm_skip(stm_in, size);
}
if (marker[1] == 0xDA)
{
if (stm_out != NULL)
i_read_compressed2(stm_in, stm_out, marker);
else
i_read_compressed(stm_in, marker);
}
else
{
stm_read(stm_in, marker, 2);
}
}
}
stm_set_read_endian(stm_in, rendian);
if (stm_out)
stm_set_write_endian(stm_out, wendian);
return ok;
}
static void i_read_gif_desc(GifDesc *desc, Stream *stm)
{
desc->width = stm_read_u16(stm);
desc->height = stm_read_u16(stm);
desc->flags = stm_read_u8(stm);
desc->bgcolor = stm_read_u8(stm);
desc->aspect = stm_read_u8(stm);
}
static void i_write_gif_desc(const GifDesc *desc, Stream *stm)
{
stm_write_u16(stm, desc->width);
stm_write_u16(stm, desc->height);
stm_write_u8(stm, desc->flags);
stm_write_u8(stm, desc->bgcolor);
stm_write_u8(stm, desc->aspect);
}
static void i_read_gif_imgdesc(GifImage *desc, Stream *stm)
{
desc->left = stm_read_u16(stm);
desc->top = stm_read_u16(stm);
desc->width = stm_read_u16(stm);
desc->height = stm_read_u16(stm);
desc->flags = stm_read_u8(stm);
}
static void i_write_gif_imgdesc(const GifImage *desc, Stream *stm)
{
stm_write_u16(stm, desc->left);
stm_write_u16(stm, desc->top);
stm_write_u16(stm, desc->width);
stm_write_u16(stm, desc->height);
stm_write_u8(stm, desc->flags);
}
static bool_t i_read_gif_image(Stream *stm_in, Stream *stm_out)
{
GifImage desc;
byte_t compress_size;
i_read_gif_imgdesc(&desc, stm_in);
if (stm_out)
i_write_gif_imgdesc(&desc, stm_out);
if (BIT_TEST(desc.flags, 7) == TRUE)
{
uint32_t size = 1 << ((desc.flags & 0x07) + 1);
size *= 3;
if (stm_out)
stm_pipe(stm_in, stm_out, size);
else
stm_skip(stm_in, size);
}
stm_read(stm_in, &compress_size, 1);
if (stm_out)
stm_write(stm_out, &compress_size, 1);
for (;;)
{
uint8_t size = stm_read_u8(stm_in);
if (stm_out)
stm_write_u8(stm_out, size);
if (size == 0)
break;
if (stm_out)
stm_pipe(stm_in, stm_out, size);
else
stm_skip(stm_in, size);
}
return TRUE;
}
static bool_t i_read_gif_extension(Stream *stm_in, Stream *stm_out)
{
byte_t type;
stm_read(stm_in, &type, 1);
if (stm_out)
stm_write(stm_out, &type, 1);
for (;;)
{
uint8_t size = stm_read_u8(stm_in);
if (stm_out)
stm_write_u8(stm_out, size);
if (size == 0)
break;
if (stm_out)
stm_pipe(stm_in, stm_out, size);
else
stm_skip(stm_in, size);
}
return TRUE;
}
static bool_t i_parse_gif(Stream *stm_in, Stream *stm_out, uint32_t *num_frames)
{
endian_t rendian = stm_get_read_endian(stm_in);
endian_t wendian = ekBIGEND;
GifDesc desc;
bool_t ok = TRUE;
cassert_no_null(num_frames);
*num_frames = 0;
stm_set_read_endian(stm_in, ekLITEND);
if (stm_out)
{
wendian = stm_get_write_endian(stm_out);
stm_set_write_endian(stm_out, ekLITEND);
}
i_read_gif_desc(&desc, stm_in);
if (stm_out)
i_write_gif_desc(&desc, stm_out);
if (BIT_TEST(desc.flags, 7) == TRUE)
{
uint32_t size = 1 << ((desc.flags & 0x07) + 1);
size *= 3;
if (stm_out)
stm_pipe(stm_in, stm_out, size);
else
stm_skip(stm_in, size);
}
for (;;)
{
byte_t type;
stm_read(stm_in, &type, 1);
if (stm_out)
stm_write(stm_out, &type, 1);
if (type == 0x21)
{
if (i_read_gif_extension(stm_in, stm_out) == FALSE)
{
ok = FALSE;
break;
}
}
else if (type == 0x2c)
{
if (i_read_gif_image(stm_in, stm_out) == FALSE)
{
ok = FALSE;
break;
}
*num_frames += 1;
}
else if (type == 0x3b)
{
break;
}
else
{
ok = FALSE;
break;
}
}
stm_set_read_endian(stm_in, rendian);
if (stm_out)
stm_set_write_endian(stm_out, wendian);
return ok;
}
static bool_t i_parse_bmp(Stream *stm_in, Stream *stm_out)
{
endian_t rendian = stm_get_read_endian(stm_in);
endian_t wendian = ekBIGEND;
uint32_t size;
bool_t ok = TRUE;
stm_set_read_endian(stm_in, ekLITEND);
if (stm_out)
{
wendian = stm_get_write_endian(stm_out);
stm_set_write_endian(stm_out, ekLITEND);
}
size = stm_read_u32(stm_in);
if (stm_out)
{
stm_write_u32(stm_out, size);
stm_pipe(stm_in, stm_out, size - 6);
}
else
{
stm_skip(stm_in, size - 6);
}
stm_set_read_endian(stm_in, rendian);
if (stm_out)
stm_set_write_endian(stm_out, wendian);
return ok;
}
static codec_t i_header(Stream *stm_in, Stream *stm_out)
{
byte_t first;
stm_read(stm_in, &first, 1);
if (first == 0x89)
{
const byte_t header[] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
byte_t rhead[7];
stm_read(stm_in, rhead, 7);
if (stm_out)
stm_write(stm_out, header, 8);
if (str_equ_cn(cast_const(rhead, char_t), cast_const(header + 1, char_t), 7) == TRUE)
return ekPNG;
return ENUM_MAX(codec_t);
}
else if (first == 0xFF)
{
const byte_t header[] = {0xFF, 0xD8};
byte_t rhead;
stm_read(stm_in, &rhead, 1);
if (stm_out)
stm_write(stm_out, header, 2);
if (rhead == header[1])
return ekJPG;
return ENUM_MAX(codec_t);
}
else if (first == 'G')
{
const char_t *header1 = "GIF87a";
const char_t *header2 = "GIF89a";
byte_t rhead[5];
stm_read(stm_in, rhead, 5);
if (stm_out)
{
stm_write(stm_out, &first, 1);
stm_write(stm_out, rhead, 5);
}
if (str_equ_cn(cast_const(rhead, char_t), header1 + 1, 5) == TRUE)
return ekGIF;
if (str_equ_cn(cast_const(rhead, char_t), header2 + 1, 5) == TRUE)
return ekGIF;
return ENUM_MAX(codec_t);
}
else if (first == 'B')
{
byte_t head[2] = {'B', '_'};
stm_read(stm_in, head + 1, 1);
if (stm_out)
stm_write(stm_out, head, 2);
if (head[1] == 'M')
return ekBMP;
else if (head[1] == 'A')
return ekBMP;
return ENUM_MAX(codec_t);
}
else if (first == 'C')
{
byte_t head[2] = {'C', '_'};
stm_read(stm_in, head + 1, 1);
if (stm_out)
stm_write(stm_out, head, 2);
if (head[1] == 'I')
return ekBMP;
else if (head[1] == 'P')
return ekBMP;
return ENUM_MAX(codec_t);
}
else if (first == 'I')
{
byte_t head[2] = {'I', '_'};
stm_read(stm_in, head + 1, 1);
if (stm_out)
stm_write(stm_out, head, 2);
if (head[1] == 'C')
return ekBMP;
return ENUM_MAX(codec_t);
}
else if (first == 'P')
{
byte_t head[2] = {'P', '_'};
stm_read(stm_in, head + 1, 1);
if (stm_out)
stm_write(stm_out, head, 2);
if (head[1] == 'T')
return ekBMP;
return ENUM_MAX(codec_t);
}
else
{
if (stm_out)
stm_write(stm_out, &first, 1);
cassert_msg(FALSE, "Unknown image encoding");
return ENUM_MAX(codec_t);
}
}
static bool_t i_parse_img(Stream *stm_in, Stream *stm_out, uint32_t *num_frames)
{
codec_t codec = i_header(stm_in, stm_out);
cassert_no_null(num_frames);
switch (codec)
{
case ekPNG:
*num_frames = 1;
return i_parse_png(stm_in, stm_out);
case ekJPG:
*num_frames = 1;
return i_parse_jpg(stm_in, stm_out);
case ekGIF:
return i_parse_gif(stm_in, stm_out, num_frames);
case ekBMP:
*num_frames = 1;
return i_parse_bmp(stm_in, stm_out);
default:
return FALSE;
}
}
bool_t _imgutil_parse(Stream *stm_in, Stream *stm_out)
{
uint32_t num_frames = 0;
return i_parse_img(stm_in, stm_out, &num_frames);
}
uint32_t _imgutil_num_frames(const byte_t *data, const uint32_t size)
{
Stream *stm = stm_from_block(data, size);
uint32_t num_frames = 0;
i_parse_img(stm, NULL, &num_frames);
stm_close(&stm);
return num_frames;
}
Palette *_imgutil_def_palette(const pixformat_t format)
{
switch (format)
{
case ekINDEX1:
return palette_binary(kCOLOR_WHITE, kCOLOR_BLACK);
case ekINDEX2:
return palette_cga2(FALSE, FALSE);
case ekINDEX4:
return palette_ega4();
case ekINDEX8:
return palette_rgb8();
case ekRGB24:
case ekRGBA32:
case ekGRAY8:
case ekFIMAGE:
cassert_default();
}
return NULL;
}
Pixbuf *_imgutil_rgba_to_rgb(const byte_t *data, const uint32_t width, const uint32_t height)
{
uint32_t i, n = width * height;
Pixbuf *pixbuf = pixbuf_create(width, height, ekRGB24);
byte_t *dest = pixbuf_data(pixbuf);
for (i = 0; i < n; ++i, data += 4, dest += 3)
{
dest[0] = data[0];
dest[1] = data[1];
dest[2] = data[2];
}
return pixbuf;
}
Pixbuf *_imgutil_rgb_to_rgba(const byte_t *data, const uint32_t width, const uint32_t height)
{
uint32_t i, n = width * height;
Pixbuf *pixbuf = pixbuf_create(width, height, ekRGBA32);
byte_t *dest = pixbuf_data(pixbuf);
for (i = 0; i < n; ++i, data += 3, dest += 4)
{
dest[0] = data[0];
dest[1] = data[1];
dest[2] = data[2];
dest[3] = 255;
}
return pixbuf;
}
Pixbuf *_imgutil_rgba_to_gray(const byte_t *data, const uint32_t width, const uint32_t height)
{
uint32_t i, n = width * height;
Pixbuf *pixbuf = pixbuf_create(width, height, ekGRAY8);
byte_t *dest = pixbuf_data(pixbuf);
for (i = 0; i < n; ++i, data += 4, dest += 1)
*dest = (byte_t)((77 * (uint32_t)data[0] + 148 * (uint32_t)data[1] + 30 * (uint32_t)data[2]) / 255);
return pixbuf;
}
Pixbuf *_imgutil_rgb_to_gray(const byte_t *data, const uint32_t width, const uint32_t height)
{
uint32_t i, n = width * height;
Pixbuf *pixbuf = pixbuf_create(width, height, ekGRAY8);
byte_t *dest = pixbuf_data(pixbuf);
for (i = 0; i < n; ++i, data += 3, dest += 1)
*dest = (byte_t)((77 * (uint32_t)data[0] + 148 * (uint32_t)data[1] + 30 * (uint32_t)data[2]) / 255);
return pixbuf;
}
Buffer *_imgutil_gray_to_rgba(const byte_t *data, const uint32_t width, const uint32_t height)
{
uint32_t i, n = width * height;
Buffer *buffer = buffer_create(n * 4);
byte_t *dest = buffer_data(buffer);
for (i = 0; i < n; ++i, data += 1, dest += 4)
{
dest[0] = data[0];
dest[1] = data[0];
dest[2] = data[0];
dest[3] = 255;
}
return buffer;
}
Buffer *_imgutil_gray_to_rgb(const byte_t *data, const uint32_t width, const uint32_t height)
{
uint32_t i, n = width * height;
Buffer *buffer = buffer_create(n * 3);
byte_t *dest = buffer_data(buffer);
for (i = 0; i < n; ++i, data += 1, dest += 3)
{
dest[0] = data[0];
dest[1] = data[0];
dest[2] = data[0];
}
return buffer;
}
Pixbuf *_imgutil_indexed_to_gray(const uint32_t width, const uint32_t height, const byte_t *pixdata, const uint32_t stride, const uint32_t ibpp, const color_t *palette)
{
Pixbuf *buffer = NULL;
byte_t *data = NULL;
byte_t ibyte;
byte_t ipos;
byte_t value;
color_t c;
byte_t imask = (byte_t)(1 << ibpp) - 1;
uint32_t j, i;
cassert(ibpp == 1 || ibpp == 2 || ibpp == 4 || ibpp == 8);
cassert_no_null(palette);
buffer = pixbuf_create(width, height, ekGRAY8);
data = pixbuf_data(buffer);
if (stride == 0)
{
uint32_t n = width * height;
for (i = 0; i < n; ++i)
{
ibyte = pixdata[i / (8 / ibpp)];
ipos = (byte_t)(i % (8 / ibpp));
value = (byte_t)((ibyte & (imask << (ipos * ibpp))) >> (ipos * ibpp));
c = palette[value];
cassert((byte_t)c == (byte_t)(c >> 8));
cassert((byte_t)c == (byte_t)(c >> 16));
*data = (byte_t)c;
data += 1;
}
}
else
{
for (j = 0; j < height; ++j)
{
for (i = 0; i < width; ++i)
{
ibyte = pixdata[i / (8 / ibpp)];
ipos = (byte_t)((8 / (uint8_t)ibpp) - 1 - (i % (8 / (uint8_t)ibpp)));
value = (byte_t)((ibyte & (imask << (ipos * ibpp))) >> (ipos * ibpp));
c = palette[value];
cassert((byte_t)c == (byte_t)(c >> 8));
cassert((byte_t)c == (byte_t)(c >> 16));
*data = (byte_t)c;
data += 1;
}
pixdata += stride;
}
}
return buffer;
}
Pixbuf *_imgutil_indexed_to_rgba(const uint32_t width, const uint32_t height, const byte_t *pixdata, const uint32_t stride, const uint32_t ibpp, const color_t *palette)
{
Pixbuf *buffer = NULL;
uint32_t *data = NULL;
byte_t ibyte;
byte_t ipos;
byte_t value;
color_t c;
byte_t imask = (byte_t)(1 << ibpp) - 1;
uint32_t j, i;
cassert(ibpp == 1 || ibpp == 2 || ibpp == 4 || ibpp == 8);
cassert_no_null(palette);
buffer = pixbuf_create(width, height, ekRGBA32);
data = cast(pixbuf_data(buffer), uint32_t);
if (stride == 0)
{
uint32_t n = width * height;
for (i = 0; i < n; ++i)
{
ibyte = pixdata[i / (8 / ibpp)];
ipos = (byte_t)(i % (8 / ibpp));
value = (byte_t)((ibyte & (imask << (ipos * ibpp))) >> (ipos * ibpp));
c = palette[value];
*data = (uint32_t)c;
data += 1;
}
}
else
{
for (j = 0; j < height; ++j)
{
for (i = 0; i < width; ++i)
{
ibyte = pixdata[i / (8 / ibpp)];
ipos = (byte_t)((8 / (uint8_t)ibpp) - 1 - (i % (8 / (uint8_t)ibpp)));
value = (byte_t)((ibyte & (imask << (ipos * ibpp))) >> (ipos * ibpp));
c = palette[value];
*data = (uint32_t)c;
data += 1;
}
pixdata += stride;
}
}
return buffer;
}
Pixbuf *_imgutil_indexed_to_rgb(const uint32_t width, const uint32_t height, const byte_t *pixdata, const uint32_t stride, const uint32_t ibpp, const color_t *palette)
{
Pixbuf *buffer = NULL;
byte_t *data = NULL;
byte_t ibyte;
byte_t ipos;
byte_t value;
color_t c;
byte_t imask = (byte_t)(1 << ibpp) - 1;
uint32_t j, i;
cassert(ibpp == 1 || ibpp == 2 || ibpp == 4 || ibpp == 8);
cassert_no_null(palette);
buffer = pixbuf_create(width, height, ekRGB24);
data = pixbuf_data(buffer);
if (stride == 0)
{
uint32_t n = width * height;
for (i = 0; i < n; ++i)
{
ibyte = pixdata[i / (8 / ibpp)];
ipos = (byte_t)(i % (8 / ibpp));
value = (byte_t)((ibyte & (imask << (ipos * ibpp))) >> (ipos * ibpp));
c = palette[value];
data[0] = (byte_t)c;
data[1] = (byte_t)(c >> 8);
data[2] = (byte_t)(c >> 16);
data += 3;
}
}
else
{
for (j = 0; j < height; ++j)
{
for (i = 0; i < width; ++i)
{
ibyte = pixdata[i / (8 / ibpp)];
ipos = (byte_t)((8 / (uint8_t)ibpp) - 1 - (i % (8 / (uint8_t)ibpp)));
value = (byte_t)((ibyte & (imask << (ipos * ibpp))) >> (ipos * ibpp));
c = palette[value];
data[0] = (byte_t)c;
data[1] = (byte_t)(c >> 8);
data[2] = (byte_t)(c >> 16);
data += 3;
}
pixdata += stride;
}
}
return buffer;
}
uint32_t _imgutil_effective_palette(const uint32_t *ipalette, const uint32_t isize, uint32_t *opalette, uint8_t *oindex)
{
uint32_t i, j;
uint32_t n = 0;
cassert(isize <= 256);
for (i = 0; i < isize; ++i)
{
for (j = 0; j < n; ++j)
{
if (ipalette[i] == opalette[j])
{
oindex[i] = (uint8_t)j;
break;
}
}
if (j == n)
{
opalette[n] = ipalette[i];
oindex[i] = (uint8_t)n;
n += 1;
}
}
return n;
}
Pixbuf *_imgutil_indexed_to_indexed(const uint32_t width, const uint32_t height, const byte_t *pixdata, const uint32_t stride, const uint32_t ibpp, const pixformat_t oformat, const uint8_t *palette_index)
{
Pixbuf *buffer = NULL;
byte_t *data = NULL;
uint32_t obpp = pixbuf_format_bpp(oformat);
byte_t imask = (byte_t)(1 << ibpp) - 1;
byte_t omask = (byte_t)(1 << obpp) - 1;
uint32_t j, i;
cassert(ibpp == 1 || ibpp == 2 || ibpp == 4 || ibpp == 8);
cassert(obpp == 1 || obpp == 2 || obpp == 4 || obpp == 8);
cassert_no_null(palette_index);
buffer = pixbuf_create(width, height, oformat);
data = pixbuf_data(buffer);
for (j = 0; j < height; ++j)
{
byte_t ibyte;
byte_t ipos;
byte_t value;
byte_t *obyte;
byte_t opos;
for (i = 0; i < width; ++i)
{
ibyte = pixdata[i / (8 / ibpp)];
ipos = (byte_t)((8 / (uint8_t)ibpp) - 1 - (i % (8 / (uint8_t)ibpp)));
value = (byte_t)((ibyte & (imask << (ipos * ibpp))) >> (ipos * ibpp));
value = palette_index[value];
cassert(value <= omask);
obyte = data + ((j * width) + i) / (8 / obpp);
opos = (byte_t)(((j * width) + i) % (8 / (uint8_t)obpp));
*obyte &= ~(omask << (opos * obpp));
*obyte |= (value << (opos * obpp));
}
pixdata += stride;
}
return buffer;
}
#define i_color(r, g, b, a) \
(color_t)(((a) << 24) | ((b) << 16) | ((g) << 8) | (r))
static Pixbuf *i_rgb_to_indexed(const byte_t *pixdata, const uint32_t width, const uint32_t height, const uint32_t bytespp, const color_t *palrgb, const uint32_t pn, Palette **palette)
{
uint32_t bpp = 0;
pixformat_t format = ENUM_MAX(pixformat_t);
Pixbuf *pixels = NULL;
uint32_t n = width * height;
byte_t omask = 0;
byte_t *destdata = NULL;
uint32_t i, j;
cassert(pn <= 256);
if (pn <= 2)
{
format = ekINDEX1;
bpp = 1;
}
else if (pn <= 4)
{
format = ekINDEX2;
bpp = 2;
}
else if (pn <= 16)
{
format = ekINDEX4;
bpp = 4;
}
else
{
format = ekINDEX8;
bpp = 8;
}
omask = (byte_t)(1 << bpp) - 1;
pixels = pixbuf_create(width, height, format);
destdata = pixbuf_data(pixels);
for (i = 0; i < n; ++i)
{
color_t c;
if (bytespp == 3)
c = i_color(pixdata[0], pixdata[1], pixdata[2], 255);
else
c = i_color(pixdata[0], pixdata[1], pixdata[2], pixdata[3]);
for (j = 0; j < pn; ++j)
{
if (palrgb[j] == c)
{
byte_t *obyte = destdata + i / (8 / bpp);
byte_t opos = (byte_t)(i % (8 / (uint8_t)bpp));
*obyte &= ~(omask << (opos * bpp));
*obyte |= (j << (opos * bpp));
break;
}
}
cassert(j < pn);
pixdata += bytespp;
}
if (palette != NULL)
{
*palette = palette_create(pn);
bmem_copy_n(palette_colors(*palette), palrgb, pn, color_t);
}
return pixels;
}
Pixbuf *_imgutil_to_indexed(const uint32_t width, const uint32_t height, const byte_t *pixdata, const uint32_t bytespp, Palette **palette)
{
uint32_t i, n = width * height;
uint32_t j, pn = 0;
const byte_t *datai = pixdata;
color_t pal[256];
for (i = 0; i < n; ++i)
{
color_t c;
if (bytespp == 3)
c = i_color(datai[0], datai[1], datai[2], 255);
else
c = i_color(datai[0], datai[1], datai[2], datai[3]);
for (j = 0; j < pn; ++j)
{
if (pal[j] == c)
break;
}
if (j == pn)
{
if (pn < 256)
{
pal[pn] = c;
pn += 1;
}
else
{
if (palette != NULL)
*palette = NULL;
return NULL;
}
}
datai += bytespp;
}
return i_rgb_to_indexed(pixdata, width, height, bytespp, pal, pn, palette);
}