summaryrefslogtreecommitdiff
path: root/source/fitz/output-tga.c
blob: 8a5952650b508935c86d1fe2d6ed4434ea1cfd4c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#include "mupdf/fitz.h"

/*
 * Write pixmap to TGA file (with or without alpha channel)
 */

static inline void tga_put_pixel(fz_context *ctx, fz_output *out, unsigned char *data, int n, int is_bgr)
{
	switch(n)
	{
	case 4: /* RGBA or BGRA */
		if (!is_bgr) {
			fz_putc(ctx, out, data[2]);
			fz_putc(ctx, out, data[1]);
			fz_putc(ctx, out, data[0]);
		} else {
			fz_putc(ctx, out, data[0]);
			fz_putc(ctx, out, data[1]);
			fz_putc(ctx, out, data[2]);
		}
		fz_putc(ctx, out, data[3]);
		break;
	case 3: /* RGB or BGR */
		if (!is_bgr) {
			fz_putc(ctx, out, data[2]);
			fz_putc(ctx, out, data[1]);
			fz_putc(ctx, out, data[0]);
		} else {
			fz_putc(ctx, out, data[0]);
			fz_putc(ctx, out, data[1]);
			fz_putc(ctx, out, data[2]);
		}
		fz_putc(ctx, out, 255);
		break;
	case 2: /* GA */
		fz_putc(ctx, out, data[0]);
		fz_putc(ctx, out, data[0]);
		fz_putc(ctx, out, data[0]);
		fz_putc(ctx, out, data[1]);
		break;
	case 1: /* GA */
		fz_putc(ctx, out, data[0]);
		fz_putc(ctx, out, data[0]);
		fz_putc(ctx, out, data[0]);
		fz_putc(ctx, out, 255);
		break;
	}
}

void
fz_save_pixmap_as_tga(fz_context *ctx, fz_pixmap *pixmap, const char *filename)
{
	fz_output *out = fz_new_output_with_path(ctx, filename, 0);
	fz_try(ctx)
		fz_write_pixmap_as_tga(ctx, out, pixmap);
	fz_always(ctx)
		fz_drop_output(ctx, out);
	fz_catch(ctx)
		fz_rethrow(ctx);
}

void
fz_write_pixmap_as_tga(fz_context *ctx, fz_output *out, fz_pixmap *pixmap)
{
	unsigned char head[18];
	int n = pixmap->n;
	int d = pixmap->alpha || n == 1 ? n : n - 1;
	int is_bgr = pixmap->colorspace == fz_device_bgr(ctx);
	int k;

	if (pixmap->colorspace && pixmap->colorspace != fz_device_gray(ctx) &&
		pixmap->colorspace != fz_device_rgb(ctx) && pixmap->colorspace != fz_device_bgr(ctx))
	{
		fz_throw(ctx, FZ_ERROR_GENERIC, "pixmap must be grayscale or rgb to write as tga");
	}

	memset(head, 0, sizeof(head));
	head[2] = n == 4 ? 10 : 11;
	head[12] = pixmap->w & 0xFF; head[13] = (pixmap->w >> 8) & 0xFF;
	head[14] = pixmap->h & 0xFF; head[15] = (pixmap->h >> 8) & 0xFF;
	head[16] = d * 8;
	head[17] = pixmap->alpha && n > 1 ? 8 : 0;
	if (pixmap->alpha && d == 2)
		head[16] = 32;

	fz_write(ctx, out, head, sizeof(head));
	for (k = 1; k <= pixmap->h; k++)
	{
		int i, j;
		unsigned char *line = pixmap->samples + pixmap->w * n * (pixmap->h - k);
		for (i = 0, j = 1; i < pixmap->w; i += j, j = 1)
		{
			for (; i + j < pixmap->w && j < 128 && !memcmp(line + i * n, line + (i + j) * n, d); j++);
			if (j > 1)
			{
				fz_putc(ctx, out, j - 1 + 128);
				tga_put_pixel(ctx, out, line + i * n, d, is_bgr);
			}
			else
			{
				for (; i + j < pixmap->w && j <= 128 && memcmp(line + (i + j - 1) * n, line + (i + j) * n, d) != 0; j++);
				if (i + j < pixmap->w || j > 128)
					j--;
				fz_putc(ctx, out, j - 1);
				for (; j > 0; j--, i++)
					tga_put_pixel(ctx, out, line + i * n, d, is_bgr);
			}
		}
	}
	fz_write(ctx, out, "\0\0\0\0\0\0\0\0TRUEVISION-XFILE.\0", 26);
}