Statistics
| Branch: | Revision:

ffmpeg / libavformat / sgi.c @ 5a757507

History | View | Annotate | Download (11.1 KB)

1
/*
2
 * SGI image format
3
 * Todd Kirby <doubleshot@pacbell.net>
4
 *
5
 * This library is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU Lesser General Public
7
 * License as published by the Free Software Foundation; either
8
 * version 2 of the License, or (at your option) any later version.
9
 *
10
 * This library is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 * Lesser General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU Lesser General Public
16
 * License along with this library; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
 */
19

    
20
#include "avformat.h"
21
#include "avio.h"
22

    
23
/* #define DEBUG */
24

    
25
/* sgi image file signature */
26
#define SGI_MAGIC 474
27

    
28
#define SGI_HEADER_SIZE 512
29

    
30
#define SGI_GRAYSCALE 1
31
#define SGI_RGB 3
32
#define SGI_RGBA 4
33

    
34
#define SGI_SINGLE_CHAN 2
35
#define SGI_MULTI_CHAN 3
36

    
37
typedef struct SGIInfo{
38
    short magic;
39
    char rle;
40
    char bytes_per_channel;
41
    unsigned short dimension;
42
    unsigned short xsize;
43
    unsigned short ysize;
44
    unsigned short zsize;
45
} SGIInfo;
46

    
47

    
48
static int sgi_probe(AVProbeData *pd)
49
{
50
    /* test for sgi magic */
51
    if (pd->buf_size >= 2 && BE_16(&pd->buf[0]) == SGI_MAGIC) {
52
        return AVPROBE_SCORE_MAX;
53
    } else {
54
        return 0;
55
    }
56
}
57

    
58
/* read sgi header fields */
59
static void read_sgi_header(ByteIOContext *f, SGIInfo *info)
60
{
61
    info->magic = (unsigned short) get_be16(f);
62
    info->rle = get_byte(f);
63
    info->bytes_per_channel = get_byte(f);
64
    info->dimension = (unsigned short)get_be16(f);
65
    info->xsize = (unsigned short) get_be16(f);
66
    info->ysize = (unsigned short) get_be16(f);
67
    info->zsize = (unsigned short) get_be16(f);
68

    
69
#ifdef DEBUG
70
    printf("sgi header fields:\n");
71
    printf("  magic: %d\n", info->magic);
72
    printf("    rle: %d\n", info->rle);
73
    printf("    bpc: %d\n", info->bytes_per_channel);
74
    printf("    dim: %d\n", info->dimension);
75
    printf("  xsize: %d\n", info->xsize);
76
    printf("  ysize: %d\n", info->ysize);
77
    printf("  zsize: %d\n", info->zsize);
78
#endif
79

    
80
    return;
81
}
82

    
83

    
84
/* read an uncompressed sgi image */
85
static int read_uncompressed_sgi(const SGIInfo *si, 
86
        AVPicture *pict, ByteIOContext *f)
87
{
88
    int x, y, z, chan_offset, ret = 0;
89
    uint8_t *dest_row, *tmp_row = NULL;
90

    
91
    tmp_row = av_malloc(si->xsize);
92

    
93
    /* skip header */ 
94
    url_fseek(f, SGI_HEADER_SIZE, SEEK_SET);
95

    
96
    pict->linesize[0] = si->xsize;
97

    
98
    for (z = 0; z < si->zsize; z++) {
99

    
100
#ifndef WORDS_BIGENDIAN
101
        /* rgba -> bgra for rgba32 on little endian cpus */
102
        if (si->zsize == 4 && z != 3) 
103
            chan_offset = 2 - z;
104
        else
105
#endif
106
            chan_offset = z;
107
            
108
        for (y = si->ysize - 1; y >= 0; y--) {
109
            dest_row = pict->data[0] + (y * si->xsize * si->zsize);
110

    
111
            if (!get_buffer(f, tmp_row, si->xsize)) {
112
                ret = -1;
113
                goto cleanup;
114
            }
115
            for (x = 0; x < si->xsize; x++) {
116
                dest_row[chan_offset] = tmp_row[x]; 
117
                dest_row += si->zsize;
118
            }
119
        }
120
    }
121

    
122
cleanup:
123
    av_free(tmp_row);
124
    return ret;
125
}
126

    
127

    
128
/* expand an rle row into a channel */
129
static void expand_rle_row(unsigned char *optr, unsigned char *iptr, 
130
        int chan_offset, int pixelstride)
131
{
132
    unsigned char pixel, count;
133
 
134
#ifndef WORDS_BIGENDIAN
135
    /* rgba -> bgra for rgba32 on little endian cpus */
136
    if (pixelstride == 4 && chan_offset != 3) {
137
       chan_offset = 2 - chan_offset;
138
    }
139
#endif
140
        
141
    optr += chan_offset;
142

    
143
    while (1) {
144
        pixel = *iptr++;
145

    
146
        if (!(count = (pixel & 0x7f))) {
147
            return;
148
        }
149
        if (pixel & 0x80) {
150
            while (count--) {
151
                *optr = *iptr;
152
                optr += pixelstride;
153
                iptr++;
154
            }
155
        } else {
156
            pixel = *iptr++;
157

    
158
            while (count--) {
159
                *optr = pixel;
160
                optr += pixelstride;
161
            }
162
        }
163
    }
164
}
165

    
166

    
167
/* read a run length encoded sgi image */
168
static int read_rle_sgi(const SGIInfo *sgi_info, 
169
        AVPicture *pict, ByteIOContext *f)
170
{
171
    uint8_t *dest_row, *rle_data = NULL;
172
    unsigned long *start_table, *length_table;
173
    int y, z, xsize, ysize, zsize, tablen; 
174
    long start_offset, run_length;
175
    int ret = 0;
176

    
177
    xsize = sgi_info->xsize;
178
    ysize = sgi_info->ysize;
179
    zsize = sgi_info->zsize;
180

    
181
    rle_data = av_malloc(xsize);
182

    
183
    /* skip header */ 
184
    url_fseek(f, SGI_HEADER_SIZE, SEEK_SET);
185

    
186
    /* size of rle offset and length tables */
187
    tablen = ysize * zsize * sizeof(long);
188

    
189
    start_table = (unsigned long *)av_malloc(tablen);
190
    length_table = (unsigned long *)av_malloc(tablen);
191

    
192
    if (!get_buffer(f, (uint8_t *)start_table, tablen)) {
193
        ret = -1;
194
        goto fail;
195
    }
196

    
197
    if (!get_buffer(f, (uint8_t *)length_table, tablen)) {
198
        ret = -1;
199
        goto fail;
200
    }
201

    
202
    for (z = 0; z < zsize; z++) {
203
        for (y = 0; y < ysize; y++) {
204
            dest_row = pict->data[0] + (ysize - 1 - y) * (xsize * zsize);
205

    
206
            start_offset = BE_32(&start_table[y + z * ysize]);
207
            run_length = BE_32(&length_table[y + z * ysize]);
208

    
209
            /* don't seek if already in the correct spot */
210
            if (url_ftell(f) != start_offset) {
211
                url_fseek(f, start_offset, SEEK_SET);
212
            }
213

    
214
            get_buffer(f, rle_data, run_length);
215
            
216
            expand_rle_row(dest_row, rle_data, z, zsize);
217
        }
218
    }
219

    
220
fail:
221
    av_free(start_table);
222
    av_free(length_table);
223
    av_free(rle_data);
224

    
225
    return ret;
226
}
227

    
228

    
229
static int sgi_read(ByteIOContext *f, 
230
        int (*alloc_cb)(void *opaque, AVImageInfo *info), void *opaque)
231
{
232
    SGIInfo sgi_info, *s = &sgi_info;
233
    AVImageInfo info1, *info = &info1;
234
    int ret;
235

    
236
    read_sgi_header(f, s);
237

    
238
    if (s->bytes_per_channel != 1) {
239
        return AVERROR_INVALIDDATA;
240
    }
241

    
242
    /* check for supported image dimensions */
243
    if (s->dimension != 2 && s->dimension != 3) {
244
        return AVERROR_INVALIDDATA;
245
    }
246

    
247
    if (s->zsize == SGI_GRAYSCALE) {
248
        info->pix_fmt = PIX_FMT_GRAY8;
249
    } else if (s->zsize == SGI_RGB) {
250
        info->pix_fmt = PIX_FMT_RGB24;
251
    } else if (s->zsize == SGI_RGBA) {
252
        info->pix_fmt = PIX_FMT_RGBA32;
253
    } else {
254
        return AVERROR_INVALIDDATA;
255
    }
256

    
257
    info->width = s->xsize;
258
    info->height = s->ysize;
259

    
260
    ret = alloc_cb(opaque, info);
261
    if (ret)
262
        return ret;
263

    
264
    if (s->rle) {
265
        return read_rle_sgi(s, &info->pict, f);
266
    } else {
267
        return read_uncompressed_sgi(s, &info->pict, f);
268
    }
269

    
270
    return 0; /* not reached */
271
}
272

    
273
#ifdef CONFIG_ENCODERS
274
static void write_sgi_header(ByteIOContext *f, const SGIInfo *info)
275
{
276
    int i;
277

    
278
    put_be16(f, SGI_MAGIC);
279
    put_byte(f, info->rle);
280
    put_byte(f, info->bytes_per_channel); 
281
    put_be16(f, info->dimension);
282
    put_be16(f, info->xsize);
283
    put_be16(f, info->ysize);
284
    put_be16(f, info->zsize);
285

    
286
    /* The rest are constant in this implementation */
287
    put_be32(f, 0L); /* pixmin */ 
288
    put_be32(f, 255L); /* pixmax */ 
289
    put_be32(f, 0L); /* dummy */ 
290

    
291
    /* name */
292
    for (i = 0; i < 80; i++) {
293
        put_byte(f, 0);
294
    }
295

    
296
    put_be32(f, 0L); /* colormap */ 
297

    
298
    /* The rest of the 512 byte header is unused. */
299
    for (i = 0; i < 404; i++) {
300
        put_byte(f, 0);
301
    }
302
}
303

    
304

    
305
static int rle_row(ByteIOContext *f, char *row, int stride, int rowsize)
306
{
307
    int length, count, i, x;
308
    char *start, repeat = 0;
309

    
310
    for (x = rowsize, length = 0; x > 0;) {
311
        start = row;
312
        row += (2 * stride);
313
        x -= 2;
314

    
315
        while (x > 0 && (row[-2 * stride] != row[-1 * stride] || 
316
                    row[-1 * stride] != row[0])) {
317
            row += stride;
318
            x--;
319
        };
320

    
321
        row -= (2 * stride);
322
        x += 2;
323

    
324
        count = (row - start) / stride;
325
        while (count > 0) {
326
            i = count > 126 ? 126 : count;
327
            count -= i;
328

    
329
            put_byte(f, 0x80 | i); 
330
            length++;
331

    
332
            while (i > 0) {
333
                put_byte(f, *start);
334
                start += stride;
335
                i--;
336
                length++;
337
            };
338
        };
339

    
340
        if (x <= 0) {
341
            break;
342
        }
343

    
344
        start = row;
345
        repeat = row[0];
346

    
347
        row += stride;
348
        x--;
349

    
350
        while (x > 0 && *row == repeat) {
351
            row += stride;
352
            x--;
353
        };
354

    
355
        count = (row - start) / stride;
356
        while (count > 0) {
357
            i = count > 126 ? 126 : count;
358
            count -= i;
359

    
360
            put_byte(f, i);
361
            length++;
362

    
363
            put_byte(f, repeat); 
364
            length++;
365
        };
366
    };
367

    
368
    length++;
369

    
370
    put_byte(f, 0); 
371
    return (length);
372
}
373

    
374

    
375
static int sgi_write(ByteIOContext *pb, AVImageInfo *info)
376
{
377
    SGIInfo sgi_info, *si = &sgi_info;
378
    long *offsettab, *lengthtab;
379
    int i, y, z;
380
    int tablesize, chan_offset;
381
    uint8_t *srcrow;
382

    
383
    si->xsize = info->width;
384
    si->ysize = info->height;
385
    si->rle = 1;
386
    si->bytes_per_channel = 1;
387
    
388
    switch(info->pix_fmt) {
389
        case PIX_FMT_GRAY8:
390
            si->dimension = SGI_SINGLE_CHAN;
391
            si->zsize = SGI_GRAYSCALE;
392
            break;
393
        case PIX_FMT_RGB24:
394
            si->dimension = SGI_MULTI_CHAN;
395
            si->zsize = SGI_RGB;
396
            break;
397
         case PIX_FMT_RGBA32:
398
            si->dimension = SGI_MULTI_CHAN;
399
            si->zsize = SGI_RGBA;
400
            break;
401
        default:
402
            return AVERROR_INVALIDDATA;
403
    }
404

    
405
    write_sgi_header(pb, si); 
406

    
407
    tablesize = si->zsize * si->ysize * sizeof(long);
408
    
409
    /* skip rle offset and length tables, write them at the end. */
410
    url_fseek(pb, tablesize * 2, SEEK_CUR);
411
    put_flush_packet(pb);
412
    
413
    lengthtab = av_malloc(tablesize);
414
    offsettab = av_malloc(tablesize);
415

    
416
    for (z = 0; z < si->zsize; z++) {
417

    
418
#ifndef WORDS_BIGENDIAN
419
        /* rgba -> bgra for rgba32 on little endian cpus */
420
        if (si->zsize == 4 && z != 3) 
421
            chan_offset = 2 - z;
422
        else
423
#endif
424
            chan_offset = z;
425
        
426
        srcrow = info->pict.data[0] + chan_offset;
427
        
428
        for (y = si->ysize -1; y >= 0; y--) {
429
            offsettab[(z * si->ysize) + y] = url_ftell(pb);
430
            lengthtab[(z * si->ysize) + y] = rle_row(pb, srcrow,
431
                    si->zsize, si->xsize);
432
            srcrow += info->pict.linesize[0]; 
433
        }
434
    }
435

    
436
    url_fseek(pb, 512, SEEK_SET);
437
    
438
    /* write offset table */
439
    for (i = 0; i < (si->ysize * si->zsize); i++) {
440
        put_be32(pb, offsettab[i]);
441
    }
442
 
443
    /* write length table */
444
    for (i = 0; i < (si->ysize * si->zsize); i++) {
445
        put_be32(pb, lengthtab[i]);
446
    }
447

    
448
    put_flush_packet(pb);
449
    
450
    av_free(lengthtab);
451
    av_free(offsettab);
452

    
453
    return 0;
454
}
455
#endif // CONFIG_ENCODERS
456

    
457
AVImageFormat sgi_image_format = {
458
    "sgi",
459
    "sgi,rgb,rgba,bw",
460
    sgi_probe,
461
    sgi_read,
462
    (1 << PIX_FMT_GRAY8) | (1 << PIX_FMT_RGB24) | (1 << PIX_FMT_RGBA32), 
463
#ifdef CONFIG_ENCODERS
464
    sgi_write,
465
#else
466
    NULL,
467
#endif // CONFIG_ENCODERS
468
};