In this case memory past the buffer bounds would have been written, potentially crashing. Replace the check with an assert since this should never happen. Also don't call exit as failing to write a file shouldn't exit Blender.
981 lines
24 KiB
C
981 lines
24 KiB
C
/* SPDX-License-Identifier: GPL-2.0-or-later
|
|
* Copyright 2001-2002 NaN Holding BV. All rights reserved. */
|
|
|
|
/** \file
|
|
* \ingroup imbuf
|
|
*/
|
|
|
|
#include <string.h>
|
|
|
|
#include "BLI_fileops.h"
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "IMB_filetype.h"
|
|
#include "IMB_imbuf.h"
|
|
#include "IMB_imbuf_types.h"
|
|
#include "imbuf.h"
|
|
|
|
#include "IMB_colormanagement.h"
|
|
#include "IMB_colormanagement_intern.h"
|
|
|
|
#define IMAGIC 0732
|
|
|
|
typedef struct {
|
|
ushort imagic; /* Stuff saved on disk. */
|
|
ushort type;
|
|
ushort dim;
|
|
ushort xsize;
|
|
ushort ysize;
|
|
ushort zsize;
|
|
uint min;
|
|
uint max;
|
|
uchar _pad1[4];
|
|
char name[80];
|
|
uint colormap;
|
|
uchar _pad2[404];
|
|
} IMAGE;
|
|
|
|
#define HEADER_SIZE 512
|
|
|
|
BLI_STATIC_ASSERT(sizeof(IMAGE) == HEADER_SIZE, "Invalid header size");
|
|
|
|
#define RINTLUM (79)
|
|
#define GINTLUM (156)
|
|
#define BINTLUM (21)
|
|
|
|
#define ILUM(r, g, b) ((int)(RINTLUM * (r) + GINTLUM * (g) + BINTLUM * (b)) >> 8)
|
|
|
|
#define OFFSET_R 0 /* this is byte order dependent */
|
|
#define OFFSET_G 1
|
|
#define OFFSET_B 2
|
|
// #define OFFSET_A 3
|
|
|
|
#define CHANOFFSET(z) (3 - (z)) /* this is byte order dependent */
|
|
|
|
// #define TYPEMASK 0xff00
|
|
#define BPPMASK 0x00ff
|
|
// #define ITYPE_VERBATIM 0x0000 /* UNUSED */
|
|
#define ITYPE_RLE 0x0100
|
|
#define ISRLE(type) (((type)&0xff00) == ITYPE_RLE)
|
|
// #define ISVERBATIM(type) (((type) & 0xff00) == ITYPE_VERBATIM)
|
|
#define BPP(type) ((type)&BPPMASK)
|
|
#define RLE(bpp) (ITYPE_RLE | (bpp))
|
|
// #define VERBATIM(bpp) (ITYPE_VERBATIM | (bpp)) /* UNUSED */
|
|
// #define IBUFSIZE(pixels) ((pixels + (pixels >> 6)) << 2) /* UNUSED */
|
|
// #define RLE_NOP 0x00
|
|
|
|
/* local struct for mem access */
|
|
typedef struct MFileOffset {
|
|
const uchar *_file_data;
|
|
uint _file_offset;
|
|
} MFileOffset;
|
|
|
|
#define MFILE_DATA(inf) ((void)0, ((inf)->_file_data + (inf)->_file_offset))
|
|
#define MFILE_STEP(inf, step) \
|
|
{ \
|
|
(inf)->_file_offset += step; \
|
|
} \
|
|
((void)0)
|
|
#define MFILE_SEEK(inf, pos) \
|
|
{ \
|
|
(inf)->_file_offset = pos; \
|
|
} \
|
|
((void)0)
|
|
|
|
/* error flags */
|
|
#define DIRTY_FLAG_EOF (1 << 0)
|
|
#define DIRTY_FLAG_ENCODING (1 << 1)
|
|
|
|
/* funcs */
|
|
static void readheader(MFileOffset *inf, IMAGE *image);
|
|
static int writeheader(FILE *outf, IMAGE *image);
|
|
|
|
static ushort getshort(MFileOffset *inf);
|
|
static uint getlong(MFileOffset *mofs);
|
|
static void putshort(FILE *outf, ushort val);
|
|
static int putlong(FILE *outf, uint val);
|
|
static int writetab(FILE *outf, uint *tab, int len);
|
|
static void readtab(MFileOffset *inf, uint *tab, int len);
|
|
|
|
static int expandrow(
|
|
uchar *optr, const uchar *optr_end, const uchar *iptr, const uchar *iptr_end, int z);
|
|
static int expandrow2(
|
|
float *optr, const float *optr_end, const uchar *iptr, const uchar *iptr_end, int z);
|
|
static void interleaverow(uchar *lptr, const uchar *cptr, int z, int n);
|
|
static void interleaverow2(float *lptr, const uchar *cptr, int z, int n);
|
|
static int compressrow(const uchar *lbuf, uchar *rlebuf, int z, int row_len);
|
|
static void lumrow(const uchar *rgbptr, uchar *lumptr, int n);
|
|
|
|
/*
|
|
* byte order independent read/write of shorts and ints.
|
|
*/
|
|
|
|
static ushort getshort(MFileOffset *inf)
|
|
{
|
|
const uchar *buf;
|
|
|
|
buf = MFILE_DATA(inf);
|
|
MFILE_STEP(inf, 2);
|
|
|
|
return ((ushort)buf[0] << 8) + ((ushort)buf[1] << 0);
|
|
}
|
|
|
|
static uint getlong(MFileOffset *mofs)
|
|
{
|
|
const uchar *buf;
|
|
|
|
buf = MFILE_DATA(mofs);
|
|
MFILE_STEP(mofs, 4);
|
|
|
|
return ((uint)buf[0] << 24) + ((uint)buf[1] << 16) + ((uint)buf[2] << 8) + ((uint)buf[3] << 0);
|
|
}
|
|
|
|
static void putshort(FILE *outf, ushort val)
|
|
{
|
|
uchar buf[2];
|
|
|
|
buf[0] = (val >> 8);
|
|
buf[1] = (val >> 0);
|
|
fwrite(buf, 2, 1, outf);
|
|
}
|
|
|
|
static int putlong(FILE *outf, uint val)
|
|
{
|
|
uchar buf[4];
|
|
|
|
buf[0] = (val >> 24);
|
|
buf[1] = (val >> 16);
|
|
buf[2] = (val >> 8);
|
|
buf[3] = (val >> 0);
|
|
return fwrite(buf, 4, 1, outf);
|
|
}
|
|
|
|
static void readheader(MFileOffset *inf, IMAGE *image)
|
|
{
|
|
memset(image, 0, sizeof(IMAGE));
|
|
image->imagic = getshort(inf);
|
|
image->type = getshort(inf);
|
|
image->dim = getshort(inf);
|
|
image->xsize = getshort(inf);
|
|
image->ysize = getshort(inf);
|
|
image->zsize = getshort(inf);
|
|
}
|
|
|
|
static int writeheader(FILE *outf, IMAGE *image)
|
|
{
|
|
IMAGE t = {0};
|
|
|
|
fwrite(&t, sizeof(IMAGE), 1, outf);
|
|
fseek(outf, 0, SEEK_SET);
|
|
putshort(outf, image->imagic);
|
|
putshort(outf, image->type);
|
|
putshort(outf, image->dim);
|
|
putshort(outf, image->xsize);
|
|
putshort(outf, image->ysize);
|
|
putshort(outf, image->zsize);
|
|
putlong(outf, image->min);
|
|
putlong(outf, image->max);
|
|
putlong(outf, 0);
|
|
return fwrite("no name", 8, 1, outf);
|
|
}
|
|
|
|
static int writetab(FILE *outf, uint *tab, int len)
|
|
{
|
|
int r = 0;
|
|
|
|
while (len) {
|
|
r = putlong(outf, *tab++);
|
|
len -= 4;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
static void readtab(MFileOffset *inf, uint *tab, int len)
|
|
{
|
|
while (len) {
|
|
*tab++ = getlong(inf);
|
|
len -= 4;
|
|
}
|
|
}
|
|
|
|
static void test_endian_zbuf(struct ImBuf *ibuf)
|
|
{
|
|
int len;
|
|
int *zval;
|
|
|
|
if (BIG_LONG(1) == 1) {
|
|
return;
|
|
}
|
|
if (ibuf->zbuf == NULL) {
|
|
return;
|
|
}
|
|
|
|
len = ibuf->x * ibuf->y;
|
|
zval = ibuf->zbuf;
|
|
|
|
while (len--) {
|
|
zval[0] = BIG_LONG(zval[0]);
|
|
zval++;
|
|
}
|
|
}
|
|
|
|
/* From misc_util: flip the bytes from x. */
|
|
#define GS(x) (((uchar *)(x))[0] << 8 | ((uchar *)(x))[1])
|
|
|
|
/* This one is only def-ed once, strangely... */
|
|
#define GSS(x) (((uchar *)(x))[1] << 8 | ((uchar *)(x))[0])
|
|
|
|
bool imb_is_a_iris(const uchar *mem, size_t size)
|
|
{
|
|
if (size < 2) {
|
|
return false;
|
|
}
|
|
return ((GS(mem) == IMAGIC) || (GSS(mem) == IMAGIC));
|
|
}
|
|
|
|
struct ImBuf *imb_loadiris(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
|
|
{
|
|
uint *base, *lptr = NULL;
|
|
float *fbase, *fptr = NULL;
|
|
uint *zbase, *zptr;
|
|
const uchar *rledat;
|
|
const uchar *mem_end = mem + size;
|
|
MFileOffset _inf_data = {mem, 0}, *inf = &_inf_data;
|
|
IMAGE image;
|
|
int bpp, rle, cur, badorder;
|
|
ImBuf *ibuf = NULL;
|
|
uchar dirty_flag = 0;
|
|
|
|
if (!imb_is_a_iris(mem, size)) {
|
|
return NULL;
|
|
}
|
|
|
|
/* Could be part of the magic check above,
|
|
* by convention this check only requests the size needed to read it's magic though. */
|
|
if (size < HEADER_SIZE) {
|
|
return NULL;
|
|
}
|
|
|
|
/* OCIO_TODO: only tested with 1 byte per pixel, not sure how to test with other settings */
|
|
colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE);
|
|
|
|
readheader(inf, &image);
|
|
if (image.imagic != IMAGIC) {
|
|
fprintf(stderr, "longimagedata: bad magic number in image file\n");
|
|
return NULL;
|
|
}
|
|
|
|
rle = ISRLE(image.type);
|
|
bpp = BPP(image.type);
|
|
if (!ELEM(bpp, 1, 2)) {
|
|
fprintf(stderr, "longimagedata: image must have 1 or 2 byte per pix chan\n");
|
|
return NULL;
|
|
}
|
|
if ((uint)image.zsize > 8) {
|
|
fprintf(stderr, "longimagedata: channels over 8 not supported\n");
|
|
return NULL;
|
|
}
|
|
|
|
const int xsize = image.xsize;
|
|
const int ysize = image.ysize;
|
|
const int zsize = image.zsize;
|
|
|
|
if (flags & IB_test) {
|
|
ibuf = IMB_allocImBuf(image.xsize, image.ysize, 8 * image.zsize, 0);
|
|
if (ibuf) {
|
|
ibuf->ftype = IMB_FTYPE_IMAGIC;
|
|
}
|
|
return ibuf;
|
|
}
|
|
|
|
if (rle) {
|
|
size_t tablen = (size_t)ysize * (size_t)zsize * sizeof(int);
|
|
MFILE_SEEK(inf, HEADER_SIZE);
|
|
|
|
uint *starttab = MEM_mallocN(tablen, "iris starttab");
|
|
uint *lengthtab = MEM_mallocN(tablen, "iris endtab");
|
|
|
|
#define MFILE_CAPACITY_AT_PTR_OK_OR_FAIL(p) \
|
|
if (UNLIKELY((p) > mem_end)) { \
|
|
dirty_flag |= DIRTY_FLAG_EOF; \
|
|
goto fail_rle; \
|
|
} \
|
|
((void)0)
|
|
|
|
MFILE_CAPACITY_AT_PTR_OK_OR_FAIL(MFILE_DATA(inf) + ((4 * 2) * tablen));
|
|
|
|
readtab(inf, starttab, tablen);
|
|
readtab(inf, lengthtab, tablen);
|
|
|
|
/* check data order */
|
|
cur = 0;
|
|
badorder = 0;
|
|
for (size_t y = 0; y < ysize; y++) {
|
|
for (size_t z = 0; z < zsize; z++) {
|
|
if (starttab[y + z * ysize] < cur) {
|
|
badorder = 1;
|
|
break;
|
|
}
|
|
cur = starttab[y + z * ysize];
|
|
}
|
|
if (badorder) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bpp == 1) {
|
|
|
|
ibuf = IMB_allocImBuf(xsize, ysize, 8 * zsize, IB_rect);
|
|
if (!ibuf) {
|
|
goto fail_rle;
|
|
}
|
|
if (ibuf->planes > 32) {
|
|
ibuf->planes = 32;
|
|
}
|
|
base = ibuf->rect;
|
|
zbase = (uint *)ibuf->zbuf;
|
|
|
|
if (badorder) {
|
|
for (size_t z = 0; z < zsize; z++) {
|
|
lptr = base;
|
|
for (size_t y = 0; y < ysize; y++) {
|
|
MFILE_SEEK(inf, starttab[y + z * ysize]);
|
|
rledat = MFILE_DATA(inf);
|
|
MFILE_STEP(inf, lengthtab[y + z * ysize]);
|
|
const uchar *rledat_next = MFILE_DATA(inf);
|
|
uint *lptr_next = lptr + xsize;
|
|
MFILE_CAPACITY_AT_PTR_OK_OR_FAIL(rledat_next);
|
|
dirty_flag |= expandrow((uchar *)lptr, (uchar *)lptr_next, rledat, rledat_next, 3 - z);
|
|
lptr = lptr_next;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
lptr = base;
|
|
zptr = zbase;
|
|
for (size_t y = 0; y < ysize; y++) {
|
|
|
|
uint *lptr_next = lptr + xsize;
|
|
uint *zptr_next = zptr + xsize;
|
|
|
|
for (size_t z = 0; z < zsize; z++) {
|
|
MFILE_SEEK(inf, starttab[y + z * ysize]);
|
|
rledat = MFILE_DATA(inf);
|
|
MFILE_STEP(inf, lengthtab[y + z * ysize]);
|
|
const uchar *rledat_next = MFILE_DATA(inf);
|
|
MFILE_CAPACITY_AT_PTR_OK_OR_FAIL(rledat_next);
|
|
if (z < 4) {
|
|
dirty_flag |= expandrow(
|
|
(uchar *)lptr, (uchar *)lptr_next, rledat, rledat_next, 3 - z);
|
|
}
|
|
else if (z < 8) {
|
|
dirty_flag |= expandrow(
|
|
(uchar *)zptr, (uchar *)zptr_next, rledat, rledat_next, 7 - z);
|
|
}
|
|
}
|
|
lptr = lptr_next;
|
|
zptr = zptr_next;
|
|
}
|
|
}
|
|
}
|
|
else { /* bpp == 2 */
|
|
|
|
ibuf = IMB_allocImBuf(xsize, ysize, 32, (flags & IB_rect) | IB_rectfloat);
|
|
if (!ibuf) {
|
|
goto fail_rle;
|
|
}
|
|
|
|
fbase = ibuf->rect_float;
|
|
|
|
if (badorder) {
|
|
for (size_t z = 0; z < zsize; z++) {
|
|
fptr = fbase;
|
|
for (size_t y = 0; y < ysize; y++) {
|
|
MFILE_SEEK(inf, starttab[y + z * ysize]);
|
|
rledat = MFILE_DATA(inf);
|
|
MFILE_STEP(inf, lengthtab[y + z * ysize]);
|
|
const uchar *rledat_next = MFILE_DATA(inf);
|
|
MFILE_CAPACITY_AT_PTR_OK_OR_FAIL(rledat_next);
|
|
float *fptr_next = fptr + (xsize * 4);
|
|
dirty_flag |= expandrow2(fptr, fptr_next, rledat, rledat_next, 3 - z);
|
|
fptr = fptr_next;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
fptr = fbase;
|
|
float *fptr_next = fptr + (xsize * 4);
|
|
|
|
for (size_t y = 0; y < ysize; y++) {
|
|
|
|
for (size_t z = 0; z < zsize; z++) {
|
|
MFILE_SEEK(inf, starttab[y + z * ysize]);
|
|
rledat = MFILE_DATA(inf);
|
|
MFILE_STEP(inf, lengthtab[y + z * ysize]);
|
|
const uchar *rledat_next = MFILE_DATA(inf);
|
|
MFILE_CAPACITY_AT_PTR_OK_OR_FAIL(rledat_next);
|
|
dirty_flag |= expandrow2(fptr, fptr_next, rledat, rledat_next, 3 - z);
|
|
}
|
|
fptr = fptr_next;
|
|
}
|
|
}
|
|
}
|
|
#undef MFILE_CAPACITY_AT_PTR_OK_OR_FAIL
|
|
fail_rle:
|
|
MEM_freeN(starttab);
|
|
MEM_freeN(lengthtab);
|
|
|
|
if (!ibuf) {
|
|
return NULL;
|
|
}
|
|
}
|
|
else {
|
|
|
|
#define MFILE_CAPACITY_AT_PTR_OK_OR_FAIL(p) \
|
|
if (UNLIKELY((p) > mem_end)) { \
|
|
dirty_flag |= DIRTY_FLAG_EOF; \
|
|
goto fail_uncompressed; \
|
|
} \
|
|
((void)0)
|
|
|
|
if (bpp == 1) {
|
|
|
|
ibuf = IMB_allocImBuf(xsize, ysize, 8 * zsize, IB_rect);
|
|
if (!ibuf) {
|
|
goto fail_uncompressed;
|
|
}
|
|
if (ibuf->planes > 32) {
|
|
ibuf->planes = 32;
|
|
}
|
|
|
|
base = ibuf->rect;
|
|
zbase = (uint *)ibuf->zbuf;
|
|
|
|
MFILE_SEEK(inf, HEADER_SIZE);
|
|
rledat = MFILE_DATA(inf);
|
|
|
|
for (size_t z = 0; z < zsize; z++) {
|
|
|
|
if (z < 4) {
|
|
lptr = base;
|
|
}
|
|
else if (z < 8) {
|
|
lptr = zbase;
|
|
}
|
|
|
|
for (size_t y = 0; y < ysize; y++) {
|
|
const uchar *rledat_next = rledat + xsize;
|
|
const int z_ofs = 3 - z;
|
|
MFILE_CAPACITY_AT_PTR_OK_OR_FAIL(rledat_next + z_ofs);
|
|
interleaverow((uchar *)lptr, rledat, z_ofs, xsize);
|
|
rledat = rledat_next;
|
|
lptr += xsize;
|
|
}
|
|
}
|
|
}
|
|
else { /* bpp == 2 */
|
|
|
|
ibuf = IMB_allocImBuf(xsize, ysize, 32, (flags & IB_rect) | IB_rectfloat);
|
|
if (!ibuf) {
|
|
goto fail_uncompressed;
|
|
}
|
|
|
|
fbase = ibuf->rect_float;
|
|
|
|
MFILE_SEEK(inf, HEADER_SIZE);
|
|
rledat = MFILE_DATA(inf);
|
|
|
|
for (size_t z = 0; z < zsize; z++) {
|
|
|
|
fptr = fbase;
|
|
|
|
for (size_t y = 0; y < ysize; y++) {
|
|
const uchar *rledat_next = rledat + xsize * 2;
|
|
const int z_ofs = 3 - z;
|
|
MFILE_CAPACITY_AT_PTR_OK_OR_FAIL(rledat_next + z_ofs);
|
|
interleaverow2(fptr, rledat, z_ofs, xsize);
|
|
rledat = rledat_next;
|
|
fptr += xsize * 4;
|
|
}
|
|
}
|
|
}
|
|
#undef MFILE_CAPACITY_AT_PTR_OK_OR_FAIL
|
|
fail_uncompressed:
|
|
if (!ibuf) {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (bpp == 1) {
|
|
uchar *rect;
|
|
|
|
if (image.zsize == 1) {
|
|
rect = (uchar *)ibuf->rect;
|
|
for (size_t x = (size_t)ibuf->x * (size_t)ibuf->y; x > 0; x--) {
|
|
rect[0] = 255;
|
|
rect[1] = rect[2] = rect[3];
|
|
rect += 4;
|
|
}
|
|
}
|
|
else if (image.zsize == 2) {
|
|
/* Gray-scale with alpha. */
|
|
rect = (uchar *)ibuf->rect;
|
|
for (size_t x = (size_t)ibuf->x * (size_t)ibuf->y; x > 0; x--) {
|
|
rect[0] = rect[2];
|
|
rect[1] = rect[2] = rect[3];
|
|
rect += 4;
|
|
}
|
|
}
|
|
else if (image.zsize == 3) {
|
|
/* add alpha */
|
|
rect = (uchar *)ibuf->rect;
|
|
for (size_t x = (size_t)ibuf->x * (size_t)ibuf->y; x > 0; x--) {
|
|
rect[0] = 255;
|
|
rect += 4;
|
|
}
|
|
}
|
|
}
|
|
else { /* bpp == 2 */
|
|
|
|
if (image.zsize == 1) {
|
|
fbase = ibuf->rect_float;
|
|
for (size_t x = (size_t)ibuf->x * (size_t)ibuf->y; x > 0; x--) {
|
|
fbase[0] = 1;
|
|
fbase[1] = fbase[2] = fbase[3];
|
|
fbase += 4;
|
|
}
|
|
}
|
|
else if (image.zsize == 2) {
|
|
/* Gray-scale with alpha. */
|
|
fbase = ibuf->rect_float;
|
|
for (size_t x = (size_t)ibuf->x * (size_t)ibuf->y; x > 0; x--) {
|
|
fbase[0] = fbase[2];
|
|
fbase[1] = fbase[2] = fbase[3];
|
|
fbase += 4;
|
|
}
|
|
}
|
|
else if (image.zsize == 3) {
|
|
/* add alpha */
|
|
fbase = ibuf->rect_float;
|
|
for (size_t x = (size_t)ibuf->x * (size_t)ibuf->y; x > 0; x--) {
|
|
fbase[0] = 1;
|
|
fbase += 4;
|
|
}
|
|
}
|
|
|
|
if (flags & IB_rect) {
|
|
IMB_rect_from_float(ibuf);
|
|
}
|
|
}
|
|
|
|
if (dirty_flag) {
|
|
fprintf(stderr, "longimagedata: corrupt file content (%d)\n", dirty_flag);
|
|
}
|
|
ibuf->ftype = IMB_FTYPE_IMAGIC;
|
|
|
|
test_endian_zbuf(ibuf);
|
|
|
|
if (ibuf->rect) {
|
|
IMB_convert_rgba_to_abgr(ibuf);
|
|
}
|
|
|
|
return ibuf;
|
|
}
|
|
|
|
/* static utility functions for longimagedata */
|
|
|
|
static void interleaverow(uchar *lptr, const uchar *cptr, int z, int n)
|
|
{
|
|
lptr += z;
|
|
while (n--) {
|
|
*lptr = *cptr++;
|
|
lptr += 4;
|
|
}
|
|
}
|
|
|
|
static void interleaverow2(float *lptr, const uchar *cptr, int z, int n)
|
|
{
|
|
lptr += z;
|
|
while (n--) {
|
|
*lptr = ((cptr[0] << 8) | (cptr[1] << 0)) / (float)0xFFFF;
|
|
cptr += 2;
|
|
lptr += 4;
|
|
}
|
|
}
|
|
|
|
static int expandrow2(
|
|
float *optr, const float *optr_end, const uchar *iptr, const uchar *iptr_end, int z)
|
|
{
|
|
ushort pixel, count;
|
|
float pixel_f;
|
|
|
|
#define EXPAND_CAPACITY_AT_INPUT_OK_OR_FAIL(iptr_next) \
|
|
if (UNLIKELY(iptr_next > iptr_end)) { \
|
|
goto fail; \
|
|
} \
|
|
((void)0)
|
|
|
|
#define EXPAND_CAPACITY_AT_OUTPUT_OK_OR_FAIL(optr_next) \
|
|
if (UNLIKELY(optr_next > optr_end)) { \
|
|
goto fail; \
|
|
} \
|
|
((void)0)
|
|
|
|
optr += z;
|
|
optr_end += z;
|
|
while (1) {
|
|
const uchar *iptr_next = iptr + 2;
|
|
EXPAND_CAPACITY_AT_INPUT_OK_OR_FAIL(iptr_next);
|
|
pixel = (iptr[0] << 8) | (iptr[1] << 0);
|
|
iptr = iptr_next;
|
|
|
|
if (!(count = (pixel & 0x7f))) {
|
|
return false;
|
|
}
|
|
const float *optr_next = optr + count;
|
|
EXPAND_CAPACITY_AT_OUTPUT_OK_OR_FAIL(optr_next);
|
|
if (pixel & 0x80) {
|
|
iptr_next = iptr + (count * 2);
|
|
EXPAND_CAPACITY_AT_INPUT_OK_OR_FAIL(iptr_next);
|
|
while (count >= 8) {
|
|
optr[0 * 4] = ((iptr[0] << 8) | (iptr[1] << 0)) / (float)0xFFFF;
|
|
optr[1 * 4] = ((iptr[2] << 8) | (iptr[3] << 0)) / (float)0xFFFF;
|
|
optr[2 * 4] = ((iptr[4] << 8) | (iptr[5] << 0)) / (float)0xFFFF;
|
|
optr[3 * 4] = ((iptr[6] << 8) | (iptr[7] << 0)) / (float)0xFFFF;
|
|
optr[4 * 4] = ((iptr[8] << 8) | (iptr[9] << 0)) / (float)0xFFFF;
|
|
optr[5 * 4] = ((iptr[10] << 8) | (iptr[11] << 0)) / (float)0xFFFF;
|
|
optr[6 * 4] = ((iptr[12] << 8) | (iptr[13] << 0)) / (float)0xFFFF;
|
|
optr[7 * 4] = ((iptr[14] << 8) | (iptr[15] << 0)) / (float)0xFFFF;
|
|
optr += 8 * 4;
|
|
iptr += 8 * 2;
|
|
count -= 8;
|
|
}
|
|
while (count--) {
|
|
*optr = ((iptr[0] << 8) | (iptr[1] << 0)) / (float)0xFFFF;
|
|
iptr += 2;
|
|
optr += 4;
|
|
}
|
|
BLI_assert(iptr == iptr_next);
|
|
}
|
|
else {
|
|
iptr_next = iptr + 2;
|
|
EXPAND_CAPACITY_AT_INPUT_OK_OR_FAIL(iptr_next);
|
|
pixel_f = ((iptr[0] << 8) | (iptr[1] << 0)) / (float)0xFFFF;
|
|
iptr = iptr_next;
|
|
|
|
while (count >= 8) {
|
|
optr[0 * 4] = pixel_f;
|
|
optr[1 * 4] = pixel_f;
|
|
optr[2 * 4] = pixel_f;
|
|
optr[3 * 4] = pixel_f;
|
|
optr[4 * 4] = pixel_f;
|
|
optr[5 * 4] = pixel_f;
|
|
optr[6 * 4] = pixel_f;
|
|
optr[7 * 4] = pixel_f;
|
|
optr += 8 * 4;
|
|
count -= 8;
|
|
}
|
|
while (count--) {
|
|
*optr = pixel_f;
|
|
optr += 4;
|
|
}
|
|
BLI_assert(iptr == iptr_next);
|
|
}
|
|
BLI_assert(optr == optr_next);
|
|
}
|
|
return false;
|
|
|
|
#undef EXPAND_CAPACITY_AT_INPUT_OK_OR_FAIL
|
|
#undef EXPAND_CAPACITY_AT_OUTPUT_OK_OR_FAIL
|
|
fail:
|
|
return DIRTY_FLAG_ENCODING;
|
|
}
|
|
|
|
static int expandrow(
|
|
uchar *optr, const uchar *optr_end, const uchar *iptr, const uchar *iptr_end, int z)
|
|
{
|
|
uchar pixel, count;
|
|
|
|
#define EXPAND_CAPACITY_AT_INPUT_OK_OR_FAIL(iptr_next) \
|
|
if (UNLIKELY(iptr_next > iptr_end)) { \
|
|
goto fail; \
|
|
} \
|
|
((void)0)
|
|
|
|
#define EXPAND_CAPACITY_AT_OUTPUT_OK_OR_FAIL(optr_next) \
|
|
if (UNLIKELY(optr_next > optr_end)) { \
|
|
goto fail; \
|
|
} \
|
|
((void)0)
|
|
|
|
optr += z;
|
|
optr_end += z;
|
|
while (1) {
|
|
const uchar *iptr_next = iptr + 1;
|
|
EXPAND_CAPACITY_AT_INPUT_OK_OR_FAIL(iptr_next);
|
|
pixel = *iptr;
|
|
iptr = iptr_next;
|
|
if (!(count = (pixel & 0x7f))) {
|
|
return false;
|
|
}
|
|
const uchar *optr_next = optr + ((int)count * 4);
|
|
EXPAND_CAPACITY_AT_OUTPUT_OK_OR_FAIL(optr_next);
|
|
|
|
if (pixel & 0x80) {
|
|
iptr_next = iptr + count;
|
|
EXPAND_CAPACITY_AT_INPUT_OK_OR_FAIL(iptr_next);
|
|
while (count >= 8) {
|
|
optr[0 * 4] = iptr[0];
|
|
optr[1 * 4] = iptr[1];
|
|
optr[2 * 4] = iptr[2];
|
|
optr[3 * 4] = iptr[3];
|
|
optr[4 * 4] = iptr[4];
|
|
optr[5 * 4] = iptr[5];
|
|
optr[6 * 4] = iptr[6];
|
|
optr[7 * 4] = iptr[7];
|
|
optr += 8 * 4;
|
|
iptr += 8;
|
|
count -= 8;
|
|
}
|
|
while (count--) {
|
|
*optr = *iptr++;
|
|
optr += 4;
|
|
}
|
|
BLI_assert(iptr == iptr_next);
|
|
}
|
|
else {
|
|
iptr_next = iptr + 1;
|
|
EXPAND_CAPACITY_AT_INPUT_OK_OR_FAIL(iptr_next);
|
|
pixel = *iptr++;
|
|
while (count >= 8) {
|
|
optr[0 * 4] = pixel;
|
|
optr[1 * 4] = pixel;
|
|
optr[2 * 4] = pixel;
|
|
optr[3 * 4] = pixel;
|
|
optr[4 * 4] = pixel;
|
|
optr[5 * 4] = pixel;
|
|
optr[6 * 4] = pixel;
|
|
optr[7 * 4] = pixel;
|
|
optr += 8 * 4;
|
|
count -= 8;
|
|
}
|
|
while (count--) {
|
|
*optr = pixel;
|
|
optr += 4;
|
|
}
|
|
BLI_assert(iptr == iptr_next);
|
|
}
|
|
BLI_assert(optr == optr_next);
|
|
}
|
|
|
|
return false;
|
|
|
|
#undef EXPAND_CAPACITY_AT_INPUT_OK_OR_FAIL
|
|
#undef EXPAND_CAPACITY_AT_OUTPUT_OK_OR_FAIL
|
|
fail:
|
|
return DIRTY_FLAG_ENCODING;
|
|
}
|
|
|
|
/**
|
|
* \param filepath: The file path to write to.
|
|
* \param lptr: an array of integers to an iris image file (each int represents one pixel).
|
|
* \param zptr: depth-buffer (optional, may be NULL).
|
|
* \param xsize: with width of the pixel-array.
|
|
* \param ysize: height of the pixel-array.
|
|
* \param zsize: specifies what kind of image file to write out.
|
|
* - 1: the luminance of the pixels are calculated,
|
|
* and a single channel black and white image is saved.
|
|
* - 3: an RGB image file is saved.
|
|
* - 4: an RGBA image file is saved.
|
|
* - 8: an RGBA image and a Z-buffer (non-null `zptr`).
|
|
*/
|
|
static bool output_iris(const char *filepath,
|
|
const uint *lptr,
|
|
const int *zptr,
|
|
const int xsize,
|
|
const int ysize,
|
|
const int zsize)
|
|
{
|
|
FILE *outf;
|
|
IMAGE *image;
|
|
int tablen, y, z, pos, len = 0;
|
|
uint *starttab, *lengthtab;
|
|
uchar *rlebuf;
|
|
uint *lumbuf;
|
|
int rlebuflen, goodwrite;
|
|
|
|
goodwrite = 1;
|
|
outf = BLI_fopen(filepath, "wb");
|
|
if (!outf) {
|
|
return 0;
|
|
}
|
|
|
|
tablen = ysize * zsize * sizeof(int);
|
|
|
|
image = (IMAGE *)MEM_mallocN(sizeof(IMAGE), "iris image");
|
|
starttab = (uint *)MEM_mallocN(tablen, "iris starttab");
|
|
lengthtab = (uint *)MEM_mallocN(tablen, "iris lengthtab");
|
|
rlebuflen = 1.05 * xsize + 10;
|
|
rlebuf = (uchar *)MEM_mallocN(rlebuflen, "iris rlebuf");
|
|
lumbuf = (uint *)MEM_mallocN(xsize * sizeof(int), "iris lumbuf");
|
|
|
|
memset(image, 0, sizeof(IMAGE));
|
|
image->imagic = IMAGIC;
|
|
image->type = RLE(1);
|
|
if (zsize > 1) {
|
|
image->dim = 3;
|
|
}
|
|
else {
|
|
image->dim = 2;
|
|
}
|
|
image->xsize = xsize;
|
|
image->ysize = ysize;
|
|
image->zsize = zsize;
|
|
image->min = 0;
|
|
image->max = 255;
|
|
goodwrite *= writeheader(outf, image);
|
|
fseek(outf, HEADER_SIZE + (2 * tablen), SEEK_SET);
|
|
pos = HEADER_SIZE + (2 * tablen);
|
|
|
|
for (y = 0; y < ysize; y++) {
|
|
for (z = 0; z < zsize; z++) {
|
|
|
|
if (zsize == 1) {
|
|
lumrow((const uchar *)lptr, (uchar *)lumbuf, xsize);
|
|
len = compressrow((const uchar *)lumbuf, rlebuf, CHANOFFSET(z), xsize);
|
|
}
|
|
else {
|
|
if (z < 4) {
|
|
len = compressrow((const uchar *)lptr, rlebuf, CHANOFFSET(z), xsize);
|
|
}
|
|
else if (z < 8 && zptr) {
|
|
len = compressrow((const uchar *)zptr, rlebuf, CHANOFFSET(z - 4), xsize);
|
|
}
|
|
}
|
|
|
|
BLI_assert_msg(len <= rlebuflen, "The length calculated for 'rlebuflen' was too small!");
|
|
|
|
goodwrite *= fwrite(rlebuf, len, 1, outf);
|
|
starttab[y + z * ysize] = pos;
|
|
lengthtab[y + z * ysize] = len;
|
|
pos += len;
|
|
}
|
|
lptr += xsize;
|
|
if (zptr) {
|
|
zptr += xsize;
|
|
}
|
|
}
|
|
|
|
fseek(outf, HEADER_SIZE, SEEK_SET);
|
|
goodwrite *= writetab(outf, starttab, tablen);
|
|
goodwrite *= writetab(outf, lengthtab, tablen);
|
|
MEM_freeN(image);
|
|
MEM_freeN(starttab);
|
|
MEM_freeN(lengthtab);
|
|
MEM_freeN(rlebuf);
|
|
MEM_freeN(lumbuf);
|
|
fclose(outf);
|
|
if (goodwrite) {
|
|
return 1;
|
|
}
|
|
|
|
fprintf(stderr, "output_iris: not enough space for image!!\n");
|
|
return 0;
|
|
}
|
|
|
|
/* static utility functions for output_iris */
|
|
|
|
static void lumrow(const uchar *rgbptr, uchar *lumptr, int n)
|
|
{
|
|
lumptr += CHANOFFSET(0);
|
|
while (n--) {
|
|
*lumptr = ILUM(rgbptr[OFFSET_R], rgbptr[OFFSET_G], rgbptr[OFFSET_B]);
|
|
lumptr += 4;
|
|
rgbptr += 4;
|
|
}
|
|
}
|
|
|
|
static int compressrow(const uchar *lbuf, uchar *rlebuf, const int z, const int row_len)
|
|
{
|
|
const uchar *iptr, *ibufend, *sptr;
|
|
uchar *optr;
|
|
short todo, cc;
|
|
int count;
|
|
|
|
lbuf += z;
|
|
iptr = lbuf;
|
|
ibufend = iptr + row_len * 4;
|
|
optr = rlebuf;
|
|
|
|
while (iptr < ibufend) {
|
|
sptr = iptr;
|
|
iptr += 8;
|
|
while ((iptr < ibufend) && ((iptr[-8] != iptr[-4]) || (iptr[-4] != iptr[0]))) {
|
|
iptr += 4;
|
|
}
|
|
iptr -= 8;
|
|
count = (iptr - sptr) / 4;
|
|
while (count) {
|
|
todo = count > 126 ? 126 : count;
|
|
count -= todo;
|
|
*optr++ = 0x80 | todo;
|
|
while (todo > 8) {
|
|
optr[0] = sptr[0 * 4];
|
|
optr[1] = sptr[1 * 4];
|
|
optr[2] = sptr[2 * 4];
|
|
optr[3] = sptr[3 * 4];
|
|
optr[4] = sptr[4 * 4];
|
|
optr[5] = sptr[5 * 4];
|
|
optr[6] = sptr[6 * 4];
|
|
optr[7] = sptr[7 * 4];
|
|
|
|
optr += 8;
|
|
sptr += 8 * 4;
|
|
todo -= 8;
|
|
}
|
|
while (todo--) {
|
|
*optr++ = *sptr;
|
|
sptr += 4;
|
|
}
|
|
}
|
|
sptr = iptr;
|
|
cc = *iptr;
|
|
iptr += 4;
|
|
while ((iptr < ibufend) && (*iptr == cc)) {
|
|
iptr += 4;
|
|
}
|
|
count = (iptr - sptr) / 4;
|
|
while (count) {
|
|
todo = count > 126 ? 126 : count;
|
|
count -= todo;
|
|
*optr++ = todo;
|
|
*optr++ = cc;
|
|
}
|
|
}
|
|
*optr++ = 0;
|
|
return optr - (uchar *)rlebuf;
|
|
}
|
|
|
|
bool imb_saveiris(struct ImBuf *ibuf, const char *filepath, int flags)
|
|
{
|
|
short zsize;
|
|
|
|
zsize = (ibuf->planes + 7) >> 3;
|
|
if (flags & IB_zbuf && ibuf->zbuf != NULL) {
|
|
zsize = 8;
|
|
}
|
|
|
|
IMB_convert_rgba_to_abgr(ibuf);
|
|
test_endian_zbuf(ibuf);
|
|
|
|
const bool ok = output_iris(filepath, ibuf->rect, ibuf->zbuf, ibuf->x, ibuf->y, zsize);
|
|
|
|
/* restore! Quite clumsy, 2 times a switch... maybe better a malloc ? */
|
|
IMB_convert_rgba_to_abgr(ibuf);
|
|
test_endian_zbuf(ibuf);
|
|
|
|
return ok;
|
|
}
|