511 lines
17 KiB
C
511 lines
17 KiB
C
#include "CgePix.h"
|
|
#include <string.h>
|
|
|
|
enum FormatType {
|
|
TYPE_UNKNOWN,
|
|
TYPE_INDEX,
|
|
TYPE_IARRAY,
|
|
TYPE_FARRAY,
|
|
TYPE_PACKED
|
|
};
|
|
|
|
struct FormatInfo {
|
|
int type;
|
|
size_t bytesPerPixel;
|
|
size_t bitsPerChannel;
|
|
size_t channels;
|
|
uint16_t masks[4];
|
|
uint8_t shifts[8];
|
|
};
|
|
|
|
static struct FormatInfo globalInfo[] = {
|
|
/* CGE_PIX_UNKNOWN */
|
|
{ TYPE_UNKNOWN, 0, 0, 0, {}, {}},
|
|
/* CGE_PIX_INDEX1 */
|
|
{ TYPE_INDEX, 0, 1, 8, {0x1}, {7, 6, 5, 4, 3, 2, 1, 0,}},
|
|
/* CGE_PIX_INDEX1_LSB */
|
|
{ TYPE_INDEX, 0, 1, 8, {0x1}, {0, 1, 2, 3, 4, 5, 6, 7,}},
|
|
/* CGE_PIX_INDEX2 */
|
|
{ TYPE_INDEX, 0, 2, 4, {0x3}, {6, 4, 2, 0,}},
|
|
/* CGE_PIX_INDEX2_LSB */
|
|
{ TYPE_INDEX, 0, 2, 4, {0x3}, {0, 2, 4, 6,}},
|
|
/* CGE_PIX_INDEX4 */
|
|
{ TYPE_INDEX, 0, 4, 2, {0xF}, {4, 0,}},
|
|
/* CGE_PIX_INDEX4_LSB */
|
|
{ TYPE_INDEX, 0, 4, 2, {0xF}, {0, 4,}},
|
|
/* CGE_PIX_INDEX8 */
|
|
{ TYPE_INDEX, 1, 8, 1, {0xFF}, {0,}},
|
|
/* CGE_PIX_R8 */
|
|
{ TYPE_IARRAY, 1, 8, 1, {0xFF}, {0,}},
|
|
/* CGE_PIX_R16 */
|
|
{ TYPE_IARRAY, 2, 16, 1, {0xFFFF}, {0,}},
|
|
/* CGE_PIX_R32F */
|
|
{ TYPE_FARRAY, 4, 32, 1, {}, {0,}},
|
|
/* CGE_PIX_RGB8 */
|
|
{ TYPE_IARRAY, 3, 8, 3, {0xFF, 0xFF, 0xFF}, {0, 1, 2,}},
|
|
/* CGE_PIX_BGR8 */
|
|
{ TYPE_IARRAY, 3, 8, 3, {0xFF, 0xFF, 0xFF}, {2, 1, 0,}},
|
|
/* CGE_PIX_RGB16 */
|
|
{ TYPE_IARRAY, 6, 16, 3, {0xFFFF, 0xFFFF, 0xFFFF}, {0, 1, 2,}},
|
|
/* CGE_PIX_BGR16 */
|
|
{ TYPE_IARRAY, 6, 16, 3, {0xFFFF, 0xFFFF, 0xFFFF}, {2, 1, 0,}},
|
|
/* CGE_PIX_RGB32F */
|
|
{ TYPE_FARRAY, 12, 32, 3, {}, {0, 1, 2,}},
|
|
/* CGE_PIX_BGR32F */
|
|
{ TYPE_FARRAY, 12, 32, 3, {}, {2, 1, 0,}},
|
|
/* CGE_PIX_RGBA8 */
|
|
{ TYPE_IARRAY, 4, 8, 4, {0xFF, 0xFF, 0xFF, 0xFF}, {0, 1, 2, 3,}},
|
|
/* CGE_PIX_BGRA8 */
|
|
{ TYPE_IARRAY, 4, 8, 4, {0xFF, 0xFF, 0xFF, 0xFF}, {2, 1, 0, 3,}},
|
|
/* CGE_PIX_ARGB8 */
|
|
{ TYPE_IARRAY, 4, 8, 4, {0xFF, 0xFF, 0xFF, 0xFF}, {1, 2, 3, 0,}},
|
|
/* CGE_PIX_ABGR8 */
|
|
{ TYPE_IARRAY, 4, 8, 4, {0xFF, 0xFF, 0xFF, 0xFF}, {3, 2, 1, 0,}},
|
|
/* CGE_PIX_RGBA16 */
|
|
{ TYPE_IARRAY, 8, 16, 4, {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF}, {0, 1, 2, 3,}},
|
|
/* CGE_PIX_BGRA16 */
|
|
{ TYPE_IARRAY, 8, 16, 4, {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF}, {2, 1, 0, 3,}},
|
|
/* CGE_PIX_ARGB16 */
|
|
{ TYPE_IARRAY, 8, 16, 4, {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF}, {1, 2, 3, 0,}},
|
|
/* CGE_PIX_ABGR16 */
|
|
{ TYPE_IARRAY, 8, 16, 4, {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF}, {3, 2, 1, 0,}},
|
|
/* CGE_PIX_RGBA32F */
|
|
{ TYPE_FARRAY, 16, 32, 4, {}, {0, 1, 2, 3,}},
|
|
/* CGE_PIX_BGRA32F */
|
|
{ TYPE_FARRAY, 16, 32, 4, {}, {2, 1, 0, 3,}},
|
|
/* CGE_PIX_ARGB32F */
|
|
{ TYPE_FARRAY, 16, 32, 4, {}, {1, 2, 3, 0,}},
|
|
/* CGE_PIX_ABGR32F */
|
|
{ TYPE_FARRAY, 16, 32, 4, {}, {3, 2, 1, 0,}},
|
|
/* CGE_PIX_RGB565 */
|
|
{ TYPE_PACKED, 2, 16, 3, {0x1F, 0x3F, 0x1F,}, {11, 5, 0,}},
|
|
/* CGE_PIX_BGR565 */
|
|
{ TYPE_PACKED, 2, 16, 3, {0x1F, 0x3F, 0x1F,}, {0, 5, 11,}},
|
|
/* CGE_PIX_RGBA5551 */
|
|
{ TYPE_PACKED, 2, 16, 4, {0x1F, 0x1F, 0x1F, 0x1,}, {11, 6, 1, 0,}},
|
|
/* CGE_PIX_BGRA5551 */
|
|
{ TYPE_PACKED, 2, 16, 4, {0x1F, 0x1F, 0x1F, 0x1,}, {1, 6, 11, 0,}},
|
|
/* CGE_PIX_ARGB1555 */
|
|
{ TYPE_PACKED, 2, 16, 4, {0x1F, 0x1F, 0x1F, 0x1,}, {10, 5, 0, 15,}},
|
|
/* CGE_PIX_ABGR1555 */
|
|
{ TYPE_PACKED, 2, 16, 4, {0x1F, 0x1F, 0x1F, 0x1,}, {0, 5, 10, 15,}},
|
|
/* CGE_PIX_RGBA1010102 */
|
|
{ TYPE_PACKED, 4, 32, 4, {0x3FF, 0x3FF, 0x3FF, 0x3}, {22, 12, 2, 0,}},
|
|
/* CGE_PIX_BGRA1010102 */
|
|
{ TYPE_PACKED, 4, 32, 4, {0x3FF, 0x3FF, 0x3FF, 0x3}, {2, 12, 22, 0,}},
|
|
/* CGE_PIX_ARGB2101010 */
|
|
{ TYPE_PACKED, 4, 32, 4, {0x3FF, 0x3FF, 0x3FF, 0x3}, {20, 10, 0, 30,}},
|
|
/* CGE_PIX_ABGR2101010 */
|
|
{ TYPE_PACKED, 4, 32, 4, {0x3FF, 0x3FF, 0x3FF, 0x3}, {0, 10, 20, 30,}},
|
|
};
|
|
|
|
static uint16_t normilize(uint16_t mask, uint16_t value) {
|
|
return ((uint32_t)(value & mask) * 65535) / mask;
|
|
}
|
|
|
|
static uint16_t denormilize(uint16_t mask, uint16_t value) {
|
|
return (((uint32_t)(value * mask)) / 65535) & mask;
|
|
}
|
|
|
|
static void fixColor(CgeColor *color, const struct FormatInfo *format, int flags) {
|
|
if (format->type == TYPE_INDEX)
|
|
return;
|
|
|
|
if (flags & CGE_PIX_NOALPHA)
|
|
color->data.channel[3] = 65535;
|
|
|
|
if (format->type != TYPE_INDEX) {
|
|
switch (format->channels) {
|
|
case 3:
|
|
color->data.channel[3] = 65535;
|
|
break;
|
|
|
|
case 1:
|
|
color->data.channel[3] = color->data.channel[0];
|
|
color->data.channel[2] = color->data.channel[0];
|
|
color->data.channel[1] = color->data.channel[0];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static size_t bestPaletteIndex(CgeColor *color, const CgeColor *palette,
|
|
const struct FormatInfo *format) {
|
|
uint32_t bestError = -1;
|
|
size_t i, bestIndex = 0, maxIndex;
|
|
CgeColor tmpColor;
|
|
|
|
maxIndex = (1 << format->bitsPerChannel);
|
|
for (i = 0; i < maxIndex; ++i) {
|
|
uint32_t currentError;
|
|
int32_t delta[4];
|
|
|
|
switch (palette[i].type) {
|
|
case CGE_COLOR_HSLA: CgeColorToHSLA(color, &tmpColor); break;
|
|
case CGE_COLOR_HSVA: CgeColorToHSVA(color, &tmpColor); break;
|
|
case CGE_COLOR_RGBA: CgeColorToRGBA(color, &tmpColor); break;
|
|
}
|
|
|
|
delta[0] = (int32_t)tmpColor.data.channel[0] - palette[i].data.channel[0];
|
|
delta[1] = (int32_t)tmpColor.data.channel[1] - palette[i].data.channel[1];
|
|
delta[2] = (int32_t)tmpColor.data.channel[2] - palette[i].data.channel[2];
|
|
delta[3] = (int32_t)tmpColor.data.channel[3] - palette[i].data.channel[3];
|
|
|
|
currentError = delta[0] * delta[0] + delta[1] * delta[1] +
|
|
delta[2] * delta[2] + delta[3] * delta[3];
|
|
|
|
if (currentError < bestError) {
|
|
bestError = currentError;
|
|
bestIndex = i;
|
|
}
|
|
}
|
|
|
|
return bestIndex;
|
|
}
|
|
|
|
static void readPacked(const struct FormatInfo *format, void *data,
|
|
CgeColor *value) {
|
|
uint32_t raw;
|
|
|
|
switch (format->bitsPerChannel) {
|
|
default: return;
|
|
case 16: raw = *(uint16_t *)data; break;
|
|
case 32: raw = *(uint32_t *)data; break;
|
|
}
|
|
|
|
switch (format->channels) {
|
|
case 4: value->data.channel[3] = normilize(format->masks[3], raw >> format->shifts[3]);
|
|
/* fallthrough */
|
|
case 3: value->data.channel[2] = normilize(format->masks[2], raw >> format->shifts[2]);
|
|
/* fallthrough */
|
|
case 2: value->data.channel[1] = normilize(format->masks[1], raw >> format->shifts[1]);
|
|
/* fallthrough */
|
|
case 1: value->data.channel[0] = normilize(format->masks[0], raw >> format->shifts[0]);
|
|
/* fallthrough */
|
|
}
|
|
}
|
|
|
|
static void writePacked(const struct FormatInfo *format, void *data,
|
|
const CgeColor *value) {
|
|
uint32_t raw = 0;
|
|
|
|
switch (format->channels) {
|
|
case 4: raw |= (uint32_t)denormilize(format->masks[3], value->data.channel[3]) << format->shifts[3];
|
|
/* fallthrough */
|
|
case 3: raw |= (uint32_t)denormilize(format->masks[2], value->data.channel[2]) << format->shifts[2];
|
|
/* fallthrough */
|
|
case 2: raw |= (uint32_t)denormilize(format->masks[1], value->data.channel[1]) << format->shifts[1];
|
|
/* fallthrough */
|
|
case 1: raw |= (uint32_t)denormilize(format->masks[0], value->data.channel[0]) << format->shifts[0];
|
|
/* fallthrough */
|
|
}
|
|
|
|
switch (format->bitsPerChannel) {
|
|
case 16: *(uint16_t *)data = raw; break;
|
|
case 32: *(uint32_t *)data = raw; break;
|
|
}
|
|
}
|
|
|
|
static void readFloatArray32(const struct FormatInfo *format, float *data,
|
|
CgeColor *value) {
|
|
float r = 0.0f, g = 0.0f, b = 0.0f, a = 0.0f;
|
|
|
|
switch (format->channels) {
|
|
case 4: a = data[format->shifts[3]];
|
|
/* fallthrough */
|
|
case 3: b = data[format->shifts[2]];
|
|
/* fallthrough */
|
|
case 2: g = data[format->shifts[1]];
|
|
/* fallthrough */
|
|
case 1: r = data[format->shifts[0]];
|
|
/* fallthrough */
|
|
}
|
|
CgeColorSetRGBAf(value, r, g, b, a);
|
|
}
|
|
|
|
static void readFloatArray(const struct FormatInfo *format, void *data,
|
|
CgeColor *value) {
|
|
switch (format->bitsPerChannel) {
|
|
default: return;
|
|
case 32: readFloatArray32(format, (float *)data, value); break;
|
|
}
|
|
}
|
|
|
|
static void writeFloatArray32(const struct FormatInfo *format, float *data,
|
|
const CgeColor *value) {
|
|
float r, g, b, a;
|
|
|
|
CgeColorRGBAf(value, &r, &g, &b, &a);
|
|
switch (format->channels) {
|
|
case 4: data[format->shifts[3]] = a;
|
|
/* fallthrough */
|
|
case 3: data[format->shifts[2]] = b;
|
|
/* fallthrough */
|
|
case 2: data[format->shifts[1]] = g;
|
|
/* fallthrough */
|
|
case 1: data[format->shifts[0]] = r;
|
|
/* fallthrough */
|
|
}
|
|
}
|
|
|
|
static void writeFloatArray(const struct FormatInfo *format, void *data,
|
|
const CgeColor *value) {
|
|
switch (format->bitsPerChannel) {
|
|
default: return;
|
|
case 32: writeFloatArray32(format, (float *)data, value); break;
|
|
}
|
|
}
|
|
|
|
static void readIntArray8(const struct FormatInfo *format, uint8_t *data,
|
|
CgeColor *value) {
|
|
switch (format->channels) {
|
|
case 4: value->data.channel[3] = normilize(format->masks[3], data[format->shifts[3]]);
|
|
/* fallthrough */
|
|
case 3: value->data.channel[2] = normilize(format->masks[2], data[format->shifts[2]]);
|
|
/* fallthrough */
|
|
case 2: value->data.channel[1] = normilize(format->masks[1], data[format->shifts[1]]);
|
|
/* fallthrough */
|
|
case 1: value->data.channel[0] = normilize(format->masks[0], data[format->shifts[0]]);
|
|
/* fallthrough */
|
|
}
|
|
}
|
|
|
|
static void readIntArray16(const struct FormatInfo *format, uint16_t *data,
|
|
CgeColor *value) {
|
|
switch (format->channels) {
|
|
case 4: value->data.channel[3] = normilize(format->masks[3], data[format->shifts[3]]);
|
|
/* fallthrough */
|
|
case 3: value->data.channel[2] = normilize(format->masks[2], data[format->shifts[2]]);
|
|
/* fallthrough */
|
|
case 2: value->data.channel[1] = normilize(format->masks[1], data[format->shifts[1]]);
|
|
/* fallthrough */
|
|
case 1: value->data.channel[0] = normilize(format->masks[0], data[format->shifts[0]]);
|
|
/* fallthrough */
|
|
}
|
|
}
|
|
|
|
static void readIntArray(const struct FormatInfo *format, void *data,
|
|
CgeColor *value) {
|
|
switch (format->bitsPerChannel) {
|
|
default: return;
|
|
case 8: readIntArray8(format, (uint8_t *)data, value); break;
|
|
case 16: readIntArray16(format, (uint16_t *)data, value); break;
|
|
}
|
|
}
|
|
|
|
static void writeIntArray8(const struct FormatInfo *format, uint8_t *data,
|
|
const CgeColor *value) {
|
|
switch (format->channels) {
|
|
case 4: data[format->shifts[3]] = denormilize(format->masks[3], value->data.channel[3]);
|
|
/* fallthrough */
|
|
case 3: data[format->shifts[2]] = denormilize(format->masks[2], value->data.channel[2]);
|
|
/* fallthrough */
|
|
case 2: data[format->shifts[1]] = denormilize(format->masks[1], value->data.channel[1]);
|
|
/* fallthrough */
|
|
case 1: data[format->shifts[0]] = denormilize(format->masks[0], value->data.channel[0]);
|
|
/* fallthrough */
|
|
}
|
|
}
|
|
|
|
static void writeIntArray16(const struct FormatInfo *format, uint16_t *data,
|
|
const CgeColor *value) {
|
|
switch (format->channels) {
|
|
case 4: data[format->shifts[3]] = denormilize(format->masks[3], value->data.channel[3]);
|
|
/* fallthrough */
|
|
case 3: data[format->shifts[2]] = denormilize(format->masks[2], value->data.channel[2]);
|
|
/* fallthrough */
|
|
case 2: data[format->shifts[1]] = denormilize(format->masks[1], value->data.channel[1]);
|
|
/* fallthrough */
|
|
case 1: data[format->shifts[0]] = denormilize(format->masks[0], value->data.channel[0]);
|
|
/* fallthrough */
|
|
}
|
|
}
|
|
|
|
static void writeIntArray(const struct FormatInfo *format, void *data,
|
|
const CgeColor *value) {
|
|
switch (format->bitsPerChannel) {
|
|
default: return;
|
|
case 8: writeIntArray8(format, (uint8_t *)data, value); break;
|
|
case 16: writeIntArray16(format, (uint16_t *)data, value); break;
|
|
}
|
|
}
|
|
|
|
static uint8_t readIndex(const struct FormatInfo *format, uint8_t *data,
|
|
uint32_t x) {
|
|
return (*data >> format->shifts[x & (format->channels - 1)]) & format->masks[0];
|
|
}
|
|
|
|
static void writeIndex(const struct FormatInfo *format, uint8_t *data, uint32_t x,
|
|
uint8_t index) {
|
|
uint8_t mask, value;
|
|
|
|
mask = ~(format->masks[0] << format->shifts[x & (format->channels - 1)]);
|
|
value = (index & format->masks[0]) << format->shifts[x & (format->channels - 1)];
|
|
*data = (*data & mask) | value;
|
|
}
|
|
|
|
static int isLittleEndian(void) {
|
|
uint16_t number = 0x0001;
|
|
return (*((uint8_t *)&number) == 0x01);
|
|
}
|
|
|
|
static void readData(const struct FormatInfo *format, void *data, uint32_t x,
|
|
CgeColor *value, const CgeColor *palette, int flags) {
|
|
switch (format->type) {
|
|
case TYPE_INDEX:
|
|
*value = palette[readIndex(format, data, x)];
|
|
break;
|
|
|
|
case TYPE_IARRAY:
|
|
readIntArray(format, data, value);
|
|
value->type = CGE_COLOR_RGBA;
|
|
break;
|
|
|
|
case TYPE_FARRAY:
|
|
readFloatArray(format, data, value);
|
|
value->type = CGE_COLOR_RGBA;
|
|
break;
|
|
|
|
case TYPE_PACKED:
|
|
readPacked(format, data, value);
|
|
value->type = CGE_COLOR_RGBA;
|
|
break;
|
|
}
|
|
|
|
fixColor(value, format, flags);
|
|
}
|
|
|
|
static void writeData(const struct FormatInfo *format, void *data, uint32_t x,
|
|
const CgeColor *value, const CgeColor *palette) {
|
|
CgeColor tmp;
|
|
CgeColorToRGBA(value, &tmp);
|
|
|
|
switch (format->type) {
|
|
case TYPE_INDEX:
|
|
writeIndex(format, data, x, bestPaletteIndex(&tmp, palette, format));
|
|
break;
|
|
|
|
case TYPE_IARRAY:
|
|
writeIntArray(format, data, &tmp);
|
|
break;
|
|
|
|
case TYPE_FARRAY:
|
|
writeFloatArray(format, data, &tmp);
|
|
break;
|
|
|
|
case TYPE_PACKED:
|
|
writePacked(format, data, &tmp);
|
|
break;
|
|
}
|
|
}
|
|
|
|
int CgePixFormatIs32Native(int format) {
|
|
switch (format) {
|
|
case CGE_PIX_BGRA8: return isLittleEndian();
|
|
case CGE_PIX_ARGB8: return !isLittleEndian();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
size_t CgePixInitLayout(uint32_t width, uint32_t height, int format, int flags,
|
|
CgePix *out) {
|
|
struct FormatInfo *lookup = globalInfo + format;
|
|
|
|
out->width = width;
|
|
out->height = height;
|
|
out->format = format;
|
|
out->flags = flags;
|
|
out->data = NULL;
|
|
out->palette = NULL;
|
|
|
|
if (lookup->type == TYPE_INDEX)
|
|
out->stride = (lookup->bitsPerChannel * width + 7) >> 3;
|
|
else
|
|
out->stride = lookup->bytesPerPixel * width;
|
|
|
|
if (flags & CGE_PIX_ALIGN32)
|
|
out->stride = (out->stride + 3) & ~(size_t)0x3;
|
|
|
|
return CgePixSize(out);
|
|
}
|
|
|
|
void CgePixView(CgePix *pix, uint32_t x, uint32_t y, uint32_t width,
|
|
uint32_t height, CgePix *out) {
|
|
memcpy(out, pix, sizeof(*pix));
|
|
|
|
if (x > pix->width)
|
|
x = pix->width;
|
|
if (y > pix->height)
|
|
y = pix->height;
|
|
|
|
if (width > pix->width - x)
|
|
width = pix->width - x;
|
|
if (height > pix->height - y)
|
|
height = pix->height - y;
|
|
|
|
out->data = CgePixAt(pix, x, y);
|
|
out->width = width;
|
|
out->height = height;
|
|
}
|
|
|
|
size_t CgePixSize(const CgePix *pix) {
|
|
return pix->stride * pix->height;
|
|
}
|
|
|
|
void *CgePixScanline(const CgePix *pix, uint32_t y) {
|
|
return (char *)(pix->data) + y * pix->stride;
|
|
}
|
|
|
|
static void *rowAt(const struct FormatInfo *format, void *data, uint32_t x) {
|
|
if (format->type == TYPE_INDEX)
|
|
return (char *)data + ((x * format->bitsPerChannel) >> 3);
|
|
return (char *)data + x * format->bytesPerPixel;
|
|
}
|
|
|
|
void *CgePixAt(const CgePix *pix, uint32_t x, uint32_t y) {
|
|
struct FormatInfo *lookup = globalInfo + pix->format;
|
|
return rowAt(lookup, CgePixScanline(pix, y), x);
|
|
}
|
|
|
|
void CgePixColor(const CgePix *pix, uint32_t x, uint32_t y, CgeColor *value) {
|
|
struct FormatInfo *lookup = globalInfo + pix->format;
|
|
readData(lookup, CgePixAt(pix, x, y), x, value, pix->palette, pix->flags);
|
|
}
|
|
|
|
void CgePixSetColor(CgePix *pix, uint32_t x, uint32_t y, const CgeColor *value) {
|
|
struct FormatInfo *lookup = globalInfo + pix->format;
|
|
writeData(lookup, CgePixAt(pix, x, y), x, value, pix->palette);
|
|
}
|
|
|
|
uint8_t CgePixIndex(const CgePix *pix, uint32_t x, uint32_t y) {
|
|
struct FormatInfo *lookup = globalInfo + pix->format;
|
|
return readIndex(lookup, CgePixAt(pix, x, y), x);
|
|
}
|
|
|
|
void CgePixSetIndex(CgePix *pix, uint32_t x, uint32_t y, uint8_t index) {
|
|
struct FormatInfo *lookup = globalInfo + pix->format;
|
|
writeIndex(lookup, CgePixAt(pix, x, y), x, index);
|
|
}
|
|
|
|
void CgePixConvertRow(void *src, int srcFormat, int srcFlags,
|
|
const CgeColor *srcPalette, void *dest, int destFormat,
|
|
int destFlags, const CgeColor *destPalette, size_t count) {
|
|
const struct FormatInfo *srcInfo, *destInfo;
|
|
size_t x;
|
|
|
|
/* Unused */
|
|
(void)destFlags;
|
|
|
|
srcInfo = globalInfo + srcFormat;
|
|
destInfo = globalInfo + destFormat;
|
|
for (x = 0; count; --count, ++x) {
|
|
void *srcAt, *destAt;
|
|
CgeColor data;
|
|
|
|
srcAt = rowAt(srcInfo, src, x);
|
|
destAt = rowAt(destInfo, dest, x);
|
|
readData(srcInfo, srcAt, x, &data, srcPalette, srcFlags);
|
|
writeData(destInfo, destAt, x, &data, destPalette);
|
|
}
|
|
}
|