1/*
2 * Copyright 2007-2008, Axel D��rfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "RAWTranslator.h"
8#include "ConfigView.h"
9#ifdef USES_LIBRAW
10#include "LibRAW.h"
11#else
12#include "RAW.h"
13#endif
14
15#include <Catalog.h>
16#include <BufferIO.h>
17#include <Messenger.h>
18#include <TranslatorRoster.h>
19
20#include <stdlib.h>
21#include <stdio.h>
22#include <string.h>
23
24#undef B_TRANSLATION_CONTEXT
25#define B_TRANSLATION_CONTEXT "RAWTranslator"
26
27
28class FreeAllocation {
29	public:
30		FreeAllocation(void* buffer)
31			:
32			fBuffer(buffer)
33		{
34		}
35
36		~FreeAllocation()
37		{
38			free(fBuffer);
39		}
40
41	private:
42		void*	fBuffer;
43};
44
45struct progress_data {
46	BMessenger	monitor;
47	BMessage	message;
48};
49
50// Extensions that ShowImage supports
51const char* kDocumentCount = "/documentCount";
52const char* kDocumentIndex = "/documentIndex";
53const char* kProgressMonitor = "/progressMonitor";
54const char* kProgressMessage = "/progressMessage";
55
56
57// The input formats that this translator supports.
58static const translation_format sInputFormats[] = {
59	{
60		RAW_IMAGE_FORMAT,
61		B_TRANSLATOR_BITMAP,
62		RAW_IN_QUALITY,
63		RAW_IN_CAPABILITY,
64		"image/x-vnd.adobe-dng",
65		"Adobe Digital Negative"
66	},
67	{
68		RAW_IMAGE_FORMAT,
69		B_TRANSLATOR_BITMAP,
70		RAW_IN_QUALITY,
71		RAW_IN_CAPABILITY,
72		"image/x-vnd.photo-raw",
73		"Digital Photo RAW image"
74	}
75};
76
77// The output formats that this translator supports.
78static const translation_format sOutputFormats[] = {
79	{
80		B_TRANSLATOR_BITMAP,
81		B_TRANSLATOR_BITMAP,
82		BITS_OUT_QUALITY,
83		BITS_OUT_CAPABILITY,
84		"image/x-be-bitmap",
85		"Be Bitmap Format (RAWTranslator)"
86	}
87};
88
89// Default settings for the Translator
90static const TranSetting sDefaultSettings[] = {
91	{B_TRANSLATOR_EXT_HEADER_ONLY, TRAN_SETTING_BOOL, false},
92	{B_TRANSLATOR_EXT_DATA_ONLY, TRAN_SETTING_BOOL, false}
93};
94
95const uint32 kNumInputFormats = sizeof(sInputFormats) / sizeof(translation_format);
96const uint32 kNumOutputFormats = sizeof(sOutputFormats) / sizeof(translation_format);
97const uint32 kNumDefaultSettings = sizeof(sDefaultSettings) / sizeof(TranSetting);
98
99const char* kShortName1 = B_TRANSLATE("RAW images");
100const char* kShortInfo1 = B_TRANSLATE("RAW image translator");
101
102
103//	#pragma mark -
104
105
106RAWTranslator::RAWTranslator()
107	: BaseTranslator(kShortName1, kShortInfo1,
108		RAW_TRANSLATOR_VERSION,
109		sInputFormats, kNumInputFormats,
110		sOutputFormats, kNumOutputFormats,
111		"RAWTranslator_Settings",
112		sDefaultSettings, kNumDefaultSettings,
113		B_TRANSLATOR_BITMAP, RAW_IMAGE_FORMAT)
114{
115}
116
117
118RAWTranslator::~RAWTranslator()
119{
120}
121
122
123status_t
124RAWTranslator::DerivedIdentify(BPositionIO *stream,
125	const translation_format *format, BMessage *settings,
126	translator_info *info, uint32 outType)
127{
128	if (!outType)
129		outType = B_TRANSLATOR_BITMAP;
130	if (outType != B_TRANSLATOR_BITMAP)
131		return B_NO_TRANSLATOR;
132
133	BBufferIO io(stream, 128 * 1024, false);
134#ifdef USES_LIBRAW
135	LibRAW raw(io);
136#else
137	DCRaw raw(io);
138#endif
139	status_t status;
140
141	try {
142		status = raw.Identify();
143	} catch (status_t error) {
144		status = error;
145	}
146
147	if (status < B_OK)
148		return B_NO_TRANSLATOR;
149
150	image_meta_info meta;
151	raw.GetMetaInfo(meta);
152
153	if (settings) {
154		int32 count = raw.CountImages();
155
156		// Add page count to ioExtension
157		settings->RemoveName(kDocumentCount);
158		settings->AddInt32(kDocumentCount, count);
159
160		// Check if a document index has been specified
161		int32 index;
162		if (settings->FindInt32(kDocumentIndex, &index) == B_OK)
163			index--;
164		else
165			index = 0;
166
167		if (index < 0 || index >= count)
168			return B_NO_TRANSLATOR;
169	}
170
171	info->type = RAW_IMAGE_FORMAT;
172	info->group = B_TRANSLATOR_BITMAP;
173	info->quality = RAW_IN_QUALITY;
174	info->capability = RAW_IN_CAPABILITY;
175	snprintf(info->name, sizeof(info->name),
176		B_TRANSLATE_COMMENT("%s RAW image", "Parameter (%s) is the name of "
177		"the manufacturer (like 'Canon')"), meta.manufacturer);
178	strcpy(info->MIME, "image/x-vnd.photo-raw");
179
180	return B_OK;
181}
182
183
184/*static*/ void
185RAWTranslator::_ProgressMonitor(const char* message, float percentage,
186	void* _data)
187{
188	progress_data& data = *(progress_data*)_data;
189
190	BMessage update(data.message);
191	update.AddString("message", message);
192	update.AddFloat("percent", percentage);
193	update.AddInt64("time", system_time());
194
195	data.monitor.SendMessage(&update);
196}
197
198
199status_t
200RAWTranslator::DerivedTranslate(BPositionIO* stream,
201	const translator_info* info, BMessage* settings,
202	uint32 outType, BPositionIO* target, int32 baseType)
203{
204	if (!outType)
205		outType = B_TRANSLATOR_BITMAP;
206	if (outType != B_TRANSLATOR_BITMAP || baseType != 0)
207		return B_NO_TRANSLATOR;
208
209	BBufferIO io(stream, 1024 * 1024, false);
210#ifdef USES_LIBRAW
211	LibRAW raw(io);
212#else
213	DCRaw raw(io);
214#endif
215
216	bool headerOnly = false;
217
218	progress_data progressData;
219	BMessenger monitor;
220
221	if (settings != NULL) {
222		settings->FindBool(B_TRANSLATOR_EXT_HEADER_ONLY, &headerOnly);
223
224		bool half;
225		if (settings->FindBool("raw:half_size", &half) == B_OK && half)
226			raw.SetHalfSize(true);
227
228		if (settings->FindMessenger(kProgressMonitor,
229				&progressData.monitor) == B_OK
230			&& settings->FindMessage(kProgressMessage,
231				&progressData.message) == B_OK) {
232			raw.SetProgressMonitor(&_ProgressMonitor, &progressData);
233			_ProgressMonitor("Reading Image Data", 0, &progressData);
234		}
235	}
236
237	int32 imageIndex = 0;
238	uint8* buffer = NULL;
239	size_t bufferSize;
240	status_t status;
241
242	try {
243		status = raw.Identify();
244
245		if (status == B_OK && settings) {
246			// Check if a document index has been specified
247			if (settings->FindInt32(kDocumentIndex, &imageIndex) == B_OK)
248				imageIndex--;
249			else
250				imageIndex = 0;
251
252			if (imageIndex < 0 || imageIndex >= (int32)raw.CountImages())
253				status = B_BAD_VALUE;
254		}
255		if (status == B_OK && !headerOnly)
256			status = raw.ReadImageAt(imageIndex, buffer, bufferSize);
257	} catch (status_t error) {
258		status = error;
259	}
260
261	if (status < B_OK)
262		return B_NO_TRANSLATOR;
263
264	FreeAllocation _(buffer);
265		// frees the buffer on destruction
266
267	image_meta_info meta;
268	raw.GetMetaInfo(meta);
269
270	image_data_info data;
271	raw.ImageAt(imageIndex, data);
272
273	if (!data.is_raw) {
274		// let others handle embedded JPEG data
275		BMemoryIO io(buffer, bufferSize);
276		BMessage buffer;
277		if (meta.flip != 1) {
278			// preserve orientation
279			if (settings == NULL)
280				settings = &buffer;
281			settings->AddInt32("exif:orientation", meta.flip);
282		}
283
284		BTranslatorRoster* roster = BTranslatorRoster::Default();
285		return roster->Translate(&io, NULL, settings, target, outType);
286	}
287
288	// retrieve EXIF data
289	off_t exifOffset;
290	size_t exifLength;
291	bool bigEndian;
292	if (settings != NULL && raw.GetEXIFTag(exifOffset, exifLength, bigEndian) == B_OK) {
293		uint8* exifBuffer = (uint8*)malloc(exifLength + 16);
294		if (exifBuffer != NULL) {
295			// add fake TIFF header to EXIF data
296			struct {
297				uint16	endian;
298				uint16	tiff_marker;
299				uint32	offset;
300				uint16	null;
301			} _PACKED header;
302			header.endian = bigEndian ? 'MM' : 'II';
303			header.tiff_marker = 42;
304			header.offset = 16;
305			header.null = 0;
306			memcpy(exifBuffer, &header, sizeof(header));
307
308			if (io.ReadAt(exifOffset, exifBuffer + 16, exifLength)
309					== (ssize_t)exifLength)
310				settings->AddData("exif", B_RAW_TYPE, exifBuffer, exifLength + 16);
311
312			free(exifBuffer);
313		}
314	}
315
316#ifdef USES_LIBRAW
317	uint32 dataSize = data.output_width * 3 * data.output_height;
318#else
319	uint32 dataSize = data.output_width * 4 * data.output_height;
320#endif
321
322	TranslatorBitmap header;
323	header.magic = B_TRANSLATOR_BITMAP;
324	header.bounds.Set(0, 0, data.output_width - 1, data.output_height - 1);
325#ifdef USES_LIBRAW
326	header.rowBytes = data.output_width * 3;
327	header.colors = B_RGB24_BIG;
328#else
329	header.rowBytes = data.output_width * 4;
330	header.colors = B_RGB32;
331#endif
332	header.dataSize = dataSize;
333
334	// write out Be's Bitmap header
335	swap_data(B_UINT32_TYPE, &header, sizeof(TranslatorBitmap),
336		B_SWAP_HOST_TO_BENDIAN);
337	ssize_t bytesWritten = target->Write(&header, sizeof(TranslatorBitmap));
338	if (bytesWritten < B_OK)
339		return bytesWritten;
340
341	if ((size_t)bytesWritten != sizeof(TranslatorBitmap))
342		return B_IO_ERROR;
343
344	if (headerOnly)
345		return B_OK;
346
347	bytesWritten = target->Write(buffer, dataSize);
348	if (bytesWritten < B_OK)
349		return bytesWritten;
350
351	if ((size_t)bytesWritten != dataSize)
352		return B_IO_ERROR;
353
354	return B_OK;
355}
356
357
358BView *
359RAWTranslator::NewConfigView(TranslatorSettings *settings)
360{
361	return new ConfigView();
362}
363
364
365//	#pragma mark -
366
367
368BTranslator *
369make_nth_translator(int32 n, image_id you, uint32 flags, ...)
370{
371	if (n != 0)
372		return NULL;
373
374	return new RAWTranslator();
375}
376
377