xref: /haiku/src/add-ons/translators/raw/RAWTranslator.cpp (revision 02354704729d38c3b078c696adc1bbbd33cbcf72)
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 
28 class 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 
45 struct progress_data {
46 	BMessenger	monitor;
47 	BMessage	message;
48 };
49 
50 // Extensions that ShowImage supports
51 const char* kDocumentCount = "/documentCount";
52 const char* kDocumentIndex = "/documentIndex";
53 const char* kProgressMonitor = "/progressMonitor";
54 const char* kProgressMessage = "/progressMessage";
55 
56 
57 // The input formats that this translator supports.
58 static 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.
78 static 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
90 static 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 
95 const uint32 kNumInputFormats = sizeof(sInputFormats) / sizeof(translation_format);
96 const uint32 kNumOutputFormats = sizeof(sOutputFormats) / sizeof(translation_format);
97 const uint32 kNumDefaultSettings = sizeof(sDefaultSettings) / sizeof(TranSetting);
98 
99 const char* kShortName1 = B_TRANSLATE("RAW images");
100 const char* kShortInfo1 = B_TRANSLATE("RAW image translator");
101 
102 
103 //	#pragma mark -
104 
105 
106 RAWTranslator::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 
118 RAWTranslator::~RAWTranslator()
119 {
120 }
121 
122 
123 status_t
124 RAWTranslator::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
185 RAWTranslator::_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 
199 status_t
200 RAWTranslator::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 
358 BView *
359 RAWTranslator::NewConfigView(TranslatorSettings *settings)
360 {
361 	return new ConfigView();
362 }
363 
364 
365 //	#pragma mark -
366 
367 
368 BTranslator *
369 make_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