1/*
2 * Copyright 2006, Haiku. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Stephan A��mus <superstippi@gmx.de>
7 */
8
9#include <malloc.h>
10#include <stdio.h>
11#include <string.h>
12#include <zlib.h>
13
14#include <Bitmap.h>
15#include <Message.h>
16
17enum {
18	COMPRESSION_NONE = 0,
19	COMPRESSION_ZLIB = 2,
20};
21
22// compress_bitmap_zlib
23bool
24compress_bitmap_zlib(const BBitmap* bitmap, void** buffer, unsigned* size)
25{
26	bool result = false;
27	if (bitmap) {
28		Bytef* src = (Bytef*)bitmap->Bits();
29		uLong srcLength = bitmap->BitsLength();
30		*size = (unsigned)ceilf(srcLength * 1.01) + 12;
31		*buffer = malloc(*size);
32		if (*buffer) {
33			int ret = compress2((Bytef*)*buffer,
34								(uLongf*)size,
35								src,
36								srcLength,
37								3);
38			if (ret == Z_OK) {
39// printf("zlib compressed %ld bytes bitmap into %d bytes (%f%%)\n",
40//	srcLength, *size, ((float)*size / (float)srcLength) * 100.0);
41				if ((unsigned)ceilf(srcLength * 1.01) + 12 != *size) {
42					void* tmpBuffer = realloc(*buffer, *size);
43					if (tmpBuffer) {
44						*buffer = tmpBuffer;
45						result = true;
46					}
47				}
48			}
49			if (ret != Z_OK || !result) {
50				// error compressing
51				free(*buffer);
52				*buffer = NULL;
53				*size = 0;
54				fprintf(stderr, "zlib compression error: %d\n", ret);
55			}
56		} else
57			*size = 0;
58	}
59	return result;
60}
61
62// decompress_bitmap_zlib
63BBitmap*
64decompress_bitmap_zlib(const void* buffer, unsigned int size,
65					   BRect frame, color_space format)
66{
67	BBitmap* bitmap = new BBitmap(frame, 0, format);
68	if (bitmap->IsValid()) {
69		if (buffer) {
70			Bytef* dst = (Bytef*)bitmap->Bits();
71			uLongf dstLength = bitmap->BitsLength();
72
73			int ret = uncompress(dst,
74								 &dstLength,
75								 (const Bytef*)buffer,
76								 (uLong)size);
77			if (ret != Z_OK || dstLength != (uint32)bitmap->BitsLength()) {
78				// decompression error!
79				fprintf(stderr, "decompress_bitmap_zlib() failed "
80								"- corrupted input buffer or file!\n");
81			}
82		} else {
83			memset(bitmap->Bits(), 0, bitmap->BitsLength());
84		}
85	} else {
86		delete bitmap;
87		bitmap = NULL;
88	}
89	return bitmap;
90}
91
92
93
94// archive_bitmap
95status_t
96archive_bitmap(const BBitmap* bitmap, BMessage* into, const char* fieldName)
97{
98	status_t ret = B_BAD_VALUE;
99	if (bitmap && bitmap->IsValid() && into) {
100		void* buffer;
101		unsigned size;
102		if (compress_bitmap_zlib(bitmap, &buffer, &size)) {
103			ret = into->AddData(fieldName, B_RAW_TYPE, buffer, size);
104			if (ret >= B_OK)
105				ret = into->AddInt32("compression", COMPRESSION_ZLIB);
106			if (ret >= B_OK)
107				ret = into->AddRect("construction bounds", bitmap->Bounds());
108			if (ret >= B_OK)
109				ret = into->AddInt32("format", bitmap->ColorSpace());
110			free(buffer);
111		}
112	}
113	return ret;
114}
115
116// extract_bitmap
117status_t
118extract_bitmap(BBitmap** bitmap, const BMessage* from, const char* fieldName)
119{
120	status_t ret = B_BAD_VALUE;
121	if (bitmap && from) {
122		*bitmap = NULL;
123		// no compression (flattened BBitmap archive)
124		BMessage bitmapArchive;
125		if ((ret = from->FindMessage(fieldName, &bitmapArchive)) >= B_OK) {
126			*bitmap = new BBitmap(&bitmapArchive);
127		}
128		// compression
129		if (!*bitmap) {
130			const void* compressedData = NULL;
131			ssize_t compressedSize = 0;
132			compressedData = NULL;
133			compressedSize = 0;
134			BRect bounds;
135			color_space format;
136			uint32 compression;
137			if (((ret = from->FindData(fieldName,
138									  B_RAW_TYPE, &compressedData,
139									  &compressedSize)) >= B_OK
140				  // this is for backward compatibility
141				  || (ret = from->FindData("current compressed data",
142									  B_RAW_TYPE, &compressedData,
143									  &compressedSize)) >= B_OK)
144				&& (ret = from->FindRect("construction bounds",
145				   						 &bounds)) >= B_OK) {
146
147				// compression defaults to NONE for backward compatibility
148				if (from->FindInt32("compression", (int32*)&compression) < B_OK)
149					compression = COMPRESSION_NONE;
150				// format defaults to B_RGBA32 for backward compatibility
151				if (from->FindInt32("format", (int32*)&format) < B_OK)
152					format = B_RGBA32;
153
154				switch (compression) {
155					case COMPRESSION_ZLIB:
156						*bitmap = decompress_bitmap_zlib(compressedData,
157														 compressedSize,
158														 bounds, format);
159						break;
160				}
161			}
162		}
163		if (*bitmap)
164			ret = (*bitmap)->InitCheck();
165		else if (ret >= B_OK)
166			ret = B_NO_MEMORY;
167		if (ret < B_OK) {
168			delete *bitmap;
169			*bitmap = NULL;
170		}
171	}
172	return ret;
173}
174