1////////////////////////////////////////////////////////////////////////////////
2//
3//	File: GIFTranslator.cpp
4//
5//	Date: December 1999
6//
7//	Author: Daniel Switkin
8//
9//	Copyright 2003 (c) by Daniel Switkin. This file is made publically available
10//	under the BSD license, with the stipulations that this complete header must
11//	remain at the top of the file indefinitely, and credit must be given to the
12//	original author in any about box using this software.
13//
14////////////////////////////////////////////////////////////////////////////////
15
16// Additional authors:	Stephan A��mus, <superstippi@gmx.de>
17//						John Scipione, <jscipione@gmail.com>
18
19#include "GIFTranslator.h"
20
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <syslog.h>
25
26#include <Application.h>
27#include <ByteOrder.h>
28#include <Catalog.h>
29#include <DataIO.h>
30#include <InterfaceDefs.h>
31#include <TranslatorAddOn.h>
32#include <TranslatorFormats.h>
33#include <TypeConstants.h>
34
35#include "GIFLoad.h"
36#include "GIFSave.h"
37#include "GIFView.h"
38#include "TranslatorWindow.h"
39
40
41#ifndef GIF_TYPE
42#define GIF_TYPE 'GIF '
43#endif
44
45#undef B_TRANSLATION_CONTEXT
46#define B_TRANSLATION_CONTEXT "GIFTranslator"
47
48
49bool debug = false;
50	// this global will be externed in other files - set once here
51	// for the entire translator
52
53bool DetermineType(BPositionIO* source, bool* isGif);
54status_t GetBitmap(BPositionIO* in, BBitmap** out);
55
56static const translation_format sInputFormats[] = {
57	{
58		GIF_TYPE,
59		B_TRANSLATOR_BITMAP,
60		GIF_IN_QUALITY,
61		GIF_IN_CAPABILITY,
62		"image/gif",
63		"GIF image"
64	},
65	{
66		B_TRANSLATOR_BITMAP,
67		B_TRANSLATOR_BITMAP,
68		BBM_IN_QUALITY,
69		BBM_IN_CAPABILITY,
70		"image/x-be-bitmap",
71		"Be Bitmap Format (GIFTranslator)"
72	}
73};
74
75static const translation_format sOutputFormats[] = {
76	{
77		GIF_TYPE,
78		B_TRANSLATOR_BITMAP,
79		GIF_OUT_QUALITY,
80		GIF_OUT_CAPABILITY,
81		"image/gif",
82		"GIF image"
83	},
84	{
85		B_TRANSLATOR_BITMAP,
86		B_TRANSLATOR_BITMAP,
87		BBM_OUT_QUALITY,
88		BBM_OUT_CAPABILITY,
89		"image/x-be-bitmap",
90		"Be Bitmap Format (GIFTranslator)"
91	}
92};
93
94// Default settings for the Translator
95static const TranSetting sDefaultSettings[] = {
96	{ B_TRANSLATOR_EXT_HEADER_ONLY, TRAN_SETTING_BOOL, false },
97	{ B_TRANSLATOR_EXT_DATA_ONLY, TRAN_SETTING_BOOL, false },
98	{ GIF_SETTING_INTERLACED, TRAN_SETTING_BOOL, false },
99	{ GIF_SETTING_USE_TRANSPARENT, TRAN_SETTING_BOOL, true },
100	{ GIF_SETTING_USE_TRANSPARENT_AUTO, TRAN_SETTING_BOOL, true },
101	{ GIF_SETTING_USE_DITHERING, TRAN_SETTING_BOOL, false },
102	{ GIF_SETTING_PALETTE_MODE, TRAN_SETTING_INT32, 3 },
103	{ GIF_SETTING_PALETTE_SIZE, TRAN_SETTING_INT32, 8 },
104	{ GIF_SETTING_TRANSPARENT_RED, TRAN_SETTING_INT32, 255 },
105	{ GIF_SETTING_TRANSPARENT_GREEN, TRAN_SETTING_INT32, 255 },
106	{ GIF_SETTING_TRANSPARENT_BLUE, TRAN_SETTING_INT32, 255 }
107};
108
109const uint32 kNumInputFormats = sizeof(sInputFormats)
110	/ sizeof(translation_format);
111const uint32 kNumOutputFormats = sizeof(sOutputFormats)
112	/ sizeof(translation_format);
113const uint32 kNumDefaultSettings = sizeof(sDefaultSettings)
114	/ sizeof(TranSetting);
115
116
117/*!	Look at first few bytes in stream to determine type - throw it back
118	if it is not a GIF or a BBitmap that we understand.
119*/
120bool
121DetermineType(BPositionIO* source, bool* isGif)
122{
123	unsigned char header[7];
124	*isGif = true;
125	if (source->Read(header, 6) != 6)
126		return false;
127
128	header[6] = 0x00;
129
130	if (strcmp((char*)header, "GIF87a") != 0
131		&& strcmp((char*)header, "GIF89a") != 0) {
132		*isGif = false;
133		int32 magic = (header[0] << 24) + (header[1] << 16) + (header[2] << 8)
134			+ header[3];
135		if (magic != B_TRANSLATOR_BITMAP)
136			return false;
137
138		source->Seek(5 * 4 - 2, SEEK_CUR);
139		color_space cs;
140		if (source->Read(&cs, 4) != 4)
141			return false;
142
143		cs = (color_space)B_BENDIAN_TO_HOST_INT32(cs);
144		if (cs != B_RGB32 && cs != B_RGBA32 && cs != B_RGB32_BIG
145			&& cs != B_RGBA32_BIG) {
146			return false;
147		}
148	}
149
150	source->Seek(0, SEEK_SET);
151	return true;
152}
153
154
155status_t
156GetBitmap(BPositionIO* in, BBitmap** out)
157{
158	TranslatorBitmap header;
159	status_t result = in->Read(&header, sizeof(header));
160	if (result != sizeof(header))
161		return B_IO_ERROR;
162
163	header.magic = B_BENDIAN_TO_HOST_INT32(header.magic);
164	header.bounds.left = B_BENDIAN_TO_HOST_FLOAT(header.bounds.left);
165	header.bounds.top = B_BENDIAN_TO_HOST_FLOAT(header.bounds.top);
166	header.bounds.right = B_BENDIAN_TO_HOST_FLOAT(header.bounds.right);
167	header.bounds.bottom = B_BENDIAN_TO_HOST_FLOAT(header.bounds.bottom);
168	header.rowBytes = B_BENDIAN_TO_HOST_INT32(header.rowBytes);
169	header.colors = (color_space)B_BENDIAN_TO_HOST_INT32(header.colors);
170	header.dataSize = B_BENDIAN_TO_HOST_INT32(header.dataSize);
171
172	// dump data from stream into a BBitmap
173	*out = new(std::nothrow) BBitmap(header.bounds, header.colors);
174	if (*out == NULL)
175		return B_NO_MEMORY;
176
177	if (!(*out)->IsValid()) {
178		delete *out;
179		return B_NO_MEMORY;
180	}
181
182	result = in->Read((*out)->Bits(), header.dataSize);
183	if (result != (status_t)header.dataSize) {
184		delete *out;
185		return B_IO_ERROR;
186	}
187
188	return B_OK;
189}
190
191
192/*!	Required identify function - may need to read entire header, not sure
193*/
194status_t
195GIFTranslator::DerivedIdentify(BPositionIO* inSource,
196	const translation_format* inFormat, BMessage* ioExtension,
197	translator_info* outInfo, uint32 outType)
198{
199	const char* debug_text = getenv("GIF_TRANSLATOR_DEBUG");
200	if (debug_text != NULL && atoi(debug_text) != 0)
201		debug = true;
202
203	if (outType == 0)
204		outType = B_TRANSLATOR_BITMAP;
205
206	if (outType != GIF_TYPE && outType != B_TRANSLATOR_BITMAP)
207		return B_NO_TRANSLATOR;
208
209	bool isGif;
210	if (!DetermineType(inSource, &isGif))
211		return B_NO_TRANSLATOR;
212
213	if (!isGif && inFormat != NULL && inFormat->type != B_TRANSLATOR_BITMAP)
214		return B_NO_TRANSLATOR;
215
216	outInfo->group = B_TRANSLATOR_BITMAP;
217	if (isGif) {
218		outInfo->type = GIF_TYPE;
219		outInfo->quality = GIF_IN_QUALITY;
220		outInfo->capability = GIF_IN_CAPABILITY;
221		strlcpy(outInfo->name, B_TRANSLATE("GIF image"), sizeof(outInfo->name));
222		strcpy(outInfo->MIME, "image/gif");
223	} else {
224		outInfo->type = B_TRANSLATOR_BITMAP;
225		outInfo->quality = BBM_IN_QUALITY;
226		outInfo->capability = BBM_IN_CAPABILITY;
227		strlcpy(outInfo->name, B_TRANSLATE("Be Bitmap Format (GIFTranslator)"),
228			sizeof(outInfo->name));
229		strcpy(outInfo->MIME, "image/x-be-bitmap");
230	}
231
232	return B_OK;
233}
234
235
236/*!	Main required function - assumes that an incoming GIF must be translated
237	to a BBitmap, and vice versa - this could be improved
238*/
239status_t
240GIFTranslator::DerivedTranslate(BPositionIO* inSource,
241	const translator_info* inInfo, BMessage* ioExtension, uint32 outType,
242	BPositionIO* outDestination, int32 baseType)
243{
244	const char* debug_text = getenv("GIF_TRANSLATOR_DEBUG");
245	if (debug_text != NULL && atoi(debug_text) != 0)
246		debug = true;
247
248	if (outType == 0)
249		outType = B_TRANSLATOR_BITMAP;
250
251	if (outType != GIF_TYPE && outType != B_TRANSLATOR_BITMAP)
252		return B_NO_TRANSLATOR;
253
254	bool isGif;
255	if (!DetermineType(inSource, &isGif))
256		return B_NO_TRANSLATOR;
257
258	if (!isGif && inInfo->type != B_TRANSLATOR_BITMAP)
259		return B_NO_TRANSLATOR;
260
261	status_t result = B_OK;
262	bigtime_t now = system_time();
263
264	if (!isGif) {
265		// BBitmap to GIF
266		BBitmap* bitmap;
267		result = GetBitmap(inSource, &bitmap);
268		if (result != B_OK)
269			return result;
270
271		GIFSave* gifSave = new(std::nothrow) GIFSave(bitmap, outDestination,
272			fSettings);
273		if (gifSave == NULL) {
274			delete bitmap;
275			return B_NO_MEMORY;
276		}
277
278		if (gifSave->fatalerror) {
279			delete gifSave;
280			delete bitmap;
281			return B_NO_MEMORY;
282		}
283		delete gifSave;
284		delete bitmap;
285	} else {
286		// GIF to BBitmap
287		GIFLoad* gifLoad = new(std::nothrow) GIFLoad(inSource, outDestination);
288		if (gifLoad == NULL)
289			return B_NO_MEMORY;
290
291		if (gifLoad->fatalerror) {
292			delete gifLoad;
293			return B_NO_MEMORY;
294		}
295		delete gifLoad;
296	}
297
298	if (debug) {
299		now = system_time() - now;
300		syslog(LOG_INFO, "Translate() - Translation took %lld microseconds\n",
301			now);
302	}
303	return B_OK;
304}
305
306
307BTranslator*
308make_nth_translator(int32 n, image_id you, uint32 flags, ...)
309{
310	if (n == 0)
311		return new(std::nothrow) GIFTranslator();
312
313	return NULL;
314}
315
316
317GIFTranslator::GIFTranslator()
318	:
319	BaseTranslator(B_TRANSLATE("GIF images"),
320	B_TRANSLATE("GIF image translator"),
321	GIF_TRANSLATOR_VERSION,
322	sInputFormats, kNumInputFormats,
323	sOutputFormats, kNumOutputFormats,
324	"GIFTranslator_Settings",
325	sDefaultSettings, kNumDefaultSettings,
326	B_TRANSLATOR_BITMAP, B_GIF_FORMAT)
327{
328}
329
330
331GIFTranslator::~GIFTranslator()
332{
333}
334
335
336BView*
337GIFTranslator::NewConfigView(TranslatorSettings* settings)
338{
339	return new(std::nothrow) GIFView(settings);
340}
341
342
343int
344main()
345{
346	BApplication app("application/x-vnd.Haiku-GIFTranslator");
347	status_t result = LaunchTranslatorWindow(new(std::nothrow) GIFTranslator,
348		B_TRANSLATE("GIF Settings"), kRectView);
349	if (result == B_OK) {
350		app.Run();
351		return 0;
352	}
353
354	return 1;
355}
356