summaryrefslogtreecommitdiff
path: root/DevIL/src-IL/src/il_rle.cpp
blob: 80e71690bafe7fae2a847b0f2167f8124cf58822 (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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
//-----------------------------------------------------------------------------
// Description: Functions for run-length encoding
//-----------------------------------------------------------------------------

// RLE code from TrueVision's TGA sample code available as Tgautils.zip at
//	ftp://ftp.truevision.com/pub/TGA.File.Format.Spec/PC.Version

#define IL_RLE_C

#include "il_internal.h"
#include "il_rle.h"

ILboolean ilRleCompressLine(ILubyte *p, ILuint n, ILubyte bpp,
			ILubyte *q, ILuint *DestWidth, ILenum CompressMode) {
	
	ILint		DiffCount;		// pixel count until two identical
	ILint		SameCount;		// number of identical adjacent pixels
	ILint		RLEBufSize = 0; // count of number of bytes encoded
	ILint		MaxRun;
	const ILint bmp_pad_to_even = (ILint)(1 - ((ILint)q - *DestWidth) % 2);

	switch( CompressMode ) {
		case IL_TGACOMP:
			MaxRun = TGA_MAX_RUN;
			break;
		case IL_SGICOMP:
			MaxRun = SGI_MAX_RUN;
			break;
		case IL_BMPCOMP:
			MaxRun = BMP_MAX_RUN;
			break;
		default:
			ilSetError(IL_INVALID_PARAM);
			return IL_FALSE;
	}

	while( n > 0 ) {	
		// Analyze pixels
		DiffCount = CountDiffPixels(p, bpp, (ILint)n > MaxRun ? MaxRun : n);
		SameCount = CountSamePixels(p, bpp, (ILint)n > MaxRun ? MaxRun : n);

		if( CompressMode == IL_BMPCOMP ) {
			ILint remaining_data = n - DiffCount - SameCount;
			if( remaining_data < 3  ) { // check if the run has gone near the end
				// no absolute run can be done
				// complete the line adding 0x01 + pixel, for each pixel
				while( remaining_data > 0 ) {
					*q++ = 0x01;
					*q++ = *p++;
					remaining_data--;
				}
				DiffCount = 0;
				SameCount = 0;
				n = 0;
			}
		}

		if( DiffCount > 0 ) { // create a raw packet (bmp absolute run)
			switch(CompressMode) {
				case IL_TGACOMP:
					*q++ = (ILbyte)(DiffCount - 1);
					break;
				case IL_BMPCOMP:
					*q++ = 0x00; RLEBufSize++;
					*q++ = (ILbyte)DiffCount;
					break;
				case IL_SGICOMP:
					*q++ = (ILbyte)(DiffCount | 0x80);
					break;
			}
			n -= DiffCount;
			RLEBufSize += (DiffCount * bpp) + 1;

			while( DiffCount > 0 ) {
				switch(bpp) {
					case 4:	*q++ = *p++;
					case 3: *q++ = *p++;
					case 2: *q++ = *p++;
					case 1: *q++ = *p++;
				}
				DiffCount--;
			}
		
			if( CompressMode == IL_BMPCOMP ) {
				if( (size_t)q % 2 == bmp_pad_to_even ) {
					*q++ = 0x00; // insert padding
				}
			}
		}

		if( SameCount > 1 ) { // create a RLE packet
			switch(CompressMode) {
				case IL_TGACOMP:
					*q++ = (ILbyte)((SameCount - 1) | 0x80);
					break;
				case IL_SGICOMP:
				case IL_BMPCOMP:
					*q++ = (ILbyte)(SameCount);
					break;
			}
			n -= SameCount;
			RLEBufSize += bpp + 1;
			p += (SameCount - 1) * bpp;
			*q++ = *p++;
			switch(bpp) {
				case 4:	*q++ = *p++;
				case 3: *q++ = *p++;
				case 2: *q++ = *p++;
				case 1: *q++ = *p++;
			}
		}
	}

	// write line termination code
	switch(CompressMode) {
		case IL_SGICOMP:
			++RLEBufSize;
			*q++ = 0;
			break;
		case IL_BMPCOMP: 
			*q++ = 0x00; RLEBufSize++;
			*q++ = 0x00; RLEBufSize++;
			break;
	}
	*DestWidth = RLEBufSize;
	
	return IL_TRUE;
}


// Compresses an entire image using run-length encoding
ILuint ilRleCompress(ILubyte *Data, ILuint Width, ILuint Height, ILuint Depth, ILubyte Bpp,
		ILubyte *Dest, ILenum CompressMode, ILuint *ScanTable) {
	ILuint DestW = 0, i, j, LineLen, Bps = Width * Bpp, SizeOfPlane = Width * Height * Bpp;

	if( ScanTable )
		imemclear(ScanTable,Depth*Height*sizeof(ILuint));
	for( j = 0; j < Depth; j++ ) {
		for( i = 0; i < Height; i++ ) {
			if( ScanTable )
				*ScanTable++ = DestW;
			ilRleCompressLine(Data + j * SizeOfPlane + i * Bps, Width, Bpp, Dest + DestW, &LineLen, CompressMode);
			DestW += LineLen;
		}
	}
	
	if( CompressMode == IL_BMPCOMP ) { // add end of image
		*(Data+DestW) = 0x00; DestW++;
		*(Data+DestW) = 0x01; DestW++;
	}

	return DestW;
}