kis_jpeg_converter.cpp
1/*
2 * Copyright (c) 2005 Cyrille Berger <cberger@cberger.net>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20#include "kis_jpeg_converter.h"
21
22#include <stdio.h>
23
24extern "C" {
25#include <iccjpeg.h>
26}
27
28#include <tqfile.h>
29
30#include <tdeapplication.h>
31#include <tdemessagebox.h>
32#include <tdelocale.h>
33
34#include <KoDocumentInfo.h>
35
36#include <tdeio/netaccess.h>
37
38#include <kis_abstract_colorspace.h>
39#include <kis_colorspace_factory_registry.h>
40#include <kis_doc.h>
41#include <kis_image.h>
42#include <kis_iterators_pixel.h>
43#include <kis_paint_layer.h>
44#include <kis_group_layer.h>
45#include <kis_meta_registry.h>
46#include <kis_profile.h>
47
48#include <kis_exif_io.h>
49
50extern "C" {
51#include <libexif/exif-loader.h>
52#include <libexif/exif-utils.h>
53}
54
55#define ICC_MARKER (JPEG_APP0 + 2) /* JPEG marker code for ICC */
56#define ICC_OVERHEAD_LEN 14 /* size of non-profile data in APP2 */
57#define MAX_BYTES_IN_MARKER 65533 /* maximum data len of a JPEG marker */
58#define MAX_DATA_BYTES_IN_MARKER (MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN)
59
60namespace {
61
62 J_COLOR_SPACE getColorTypeforColorSpace( KisColorSpace * cs)
63 {
64 if ( cs->id() == KisID("GRAYA") || cs->id() == KisID("GRAYA16") )
65 {
66 return JCS_GRAYSCALE;
67 }
68 if ( cs->id() == KisID("RGBA") || cs->id() == KisID("RGBA16") )
69 {
70 return JCS_RGB;
71 }
72 if ( cs->id() == KisID("CMYK") || cs->id() == KisID("CMYK16") )
73 {
74 return JCS_CMYK;
75 }
76 KMessageBox::error(0, i18n("Cannot export images in %1.\n").arg(cs->id().name()) ) ;
77 return JCS_UNKNOWN;
78 }
79
80 TQString getColorSpaceForColorType(J_COLOR_SPACE color_type) {
81 kdDebug(41008) << "color_type = " << color_type << endl;
82 if(color_type == JCS_GRAYSCALE)
83 {
84 return "GRAYA";
85 } else if(color_type == JCS_RGB) {
86 return "RGBA";
87 } else if(color_type == JCS_CMYK) {
88 return "CMYK";
89 }
90 return "";
91 }
92
93}
94
95KisJPEGConverter::KisJPEGConverter(KisDoc *doc, KisUndoAdapter *adapter)
96{
97 m_doc = doc;
98 m_adapter = adapter;
99 m_job = 0;
100 m_stop = false;
101}
102
103KisJPEGConverter::~KisJPEGConverter()
104{
105}
106
107KisImageBuilder_Result KisJPEGConverter::decode(const KURL& uri)
108{
109 struct jpeg_decompress_struct cinfo;
110 struct jpeg_error_mgr jerr;
111
112 cinfo.err = jpeg_std_error(&jerr);
113 jpeg_create_decompress(&cinfo);
114
115 // open the file
116 FILE *fp = fopen(TQFile::encodeName(uri.path()), "rb");
117 if (!fp)
118 {
119 return (KisImageBuilder_RESULT_NOT_EXIST);
120 }
121 jpeg_stdio_src(&cinfo, fp);
122
123 jpeg_save_markers (&cinfo, JPEG_COM, 0xFFFF);
124 /* Save APP0..APP15 markers */
125 for (int m = 0; m < 16; m++)
126 jpeg_save_markers (&cinfo, JPEG_APP0 + m, 0xFFFF);
127
128
129// setup_read_icc_profile(&cinfo);
130 // read header
131 jpeg_read_header(&cinfo, true);
132
133 // start reading
134 jpeg_start_decompress(&cinfo);
135
136 // Get the colorspace
137 TQString csName = getColorSpaceForColorType(cinfo.out_color_space);
138 if(csName.isEmpty()) {
139 kdDebug(41008) << "unsupported colorspace : " << cinfo.out_color_space << endl;
140 jpeg_destroy_decompress(&cinfo);
141 fclose(fp);
142 return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
143 }
144 uchar* profile_data;
145 uint profile_len;
146 KisProfile* profile = 0;
147 TQByteArray profile_rawdata;
148 if( read_icc_profile (&cinfo, &profile_data, &profile_len))
149 {
150 profile_rawdata.resize(profile_len);
151 memcpy(profile_rawdata.data(), profile_data, profile_len);
152 cmsHPROFILE hProfile = cmsOpenProfileFromMem(profile_data, (DWORD)profile_len);
153
154 if (hProfile != (cmsHPROFILE) NULL) {
155 profile = new KisProfile( profile_rawdata);
156 TQ_CHECK_PTR(profile);
157 kdDebug(41008) << "profile name: " << profile->productName() << " profile description: " << profile->productDescription() << " information sur le produit: " << profile->productInfo() << endl;
158 if(!profile->isSuitableForOutput())
159 {
160 kdDebug(41008) << "the profile is not suitable for output and therefore cannot be used in chalk, we need to convert the image to a standard profile" << endl; // TODO: in ko2 popup a selection menu to inform the user
161 }
162 }
163 }
164
165 // Retrieve a pointer to the colorspace
166 KisColorSpace* cs;
167 if (profile && profile->isSuitableForOutput())
168 {
169 kdDebug(41008) << "image has embedded profile: " << profile -> productName() << "\n";
170 cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(csName, profile);
171 }
172 else
173 cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID(csName,""),"");
174
175 if(cs == 0)
176 {
177 kdDebug(41008) << "unknown colorspace" << endl;
178 jpeg_destroy_decompress(&cinfo);
179 fclose(fp);
180 return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
181 }
182
183 // Create the cmsTransform if needed
184
185 cmsHTRANSFORM transform = 0;
186 if(profile && !profile->isSuitableForOutput())
187 {
188 transform = cmsCreateTransform(profile->profile(), cs->colorSpaceType(),
189 cs->getProfile()->profile() , cs->colorSpaceType(),
190 INTENT_PERCEPTUAL, 0);
191 }
192
193 // Creating the KisImageSP
194 if( ! m_img) {
195 m_img = new KisImage(m_doc->undoAdapter(), cinfo.image_width, cinfo.image_height, cs, "built image");
196 TQ_CHECK_PTR(m_img);
197 if(profile && !profile->isSuitableForOutput())
198 {
199 m_img -> addAnnotation( new KisAnnotation( profile->productName(), "", profile_rawdata) );
200 }
201 }
202
203 KisPaintLayerSP layer = new KisPaintLayer(m_img, m_img -> nextLayerName(), TQ_UINT8_MAX);
204
205 // Read exif information if any
206
207 // Read data
208 JSAMPROW row_pointer = new JSAMPLE[cinfo.image_width *cinfo.num_components];
209
210 for (; cinfo.output_scanline < cinfo.image_height;) {
211 KisHLineIterator it = layer->paintDevice()->createHLineIterator(0, cinfo.output_scanline, cinfo.image_width, true);
212 jpeg_read_scanlines(&cinfo, &row_pointer, 1);
213 TQ_UINT8 *src = row_pointer;
214 switch(cinfo.out_color_space)
215 {
216 case JCS_GRAYSCALE:
217 while (!it.isDone()) {
218 TQ_UINT8 *d = it.rawData();
219 d[0] = *(src++);
220 if(transform) cmsDoTransform(transform, d, d, 1);
221 d[1] = TQ_UINT8_MAX;
222 ++it;
223 }
224 break;
225 case JCS_RGB:
226 while (!it.isDone()) {
227 TQ_UINT8 *d = it.rawData();
228 d[2] = *(src++);
229 d[1] = *(src++);
230 d[0] = *(src++);
231 if(transform) cmsDoTransform(transform, d, d, 1);
232 d[3] = TQ_UINT8_MAX;
233 ++it;
234 }
235 break;
236 case JCS_CMYK:
237 while (!it.isDone()) {
238 TQ_UINT8 *d = it.rawData();
239 d[0] = TQ_UINT8_MAX - *(src++);
240 d[1] = TQ_UINT8_MAX - *(src++);
241 d[2] = TQ_UINT8_MAX - *(src++);
242 d[3] = TQ_UINT8_MAX - *(src++);
243 if(transform) cmsDoTransform(transform, d, d, 1);
244 d[4] = TQ_UINT8_MAX;
245 ++it;
246 }
247 break;
248 default:
249 return KisImageBuilder_RESULT_UNSUPPORTED;
250 }
251 }
252
253 m_img->addLayer(layer.data(), m_img->rootLayer(), 0);
254
255 // Read exif informations
256
257 kdDebug(41008) << "Looking for exif information" << endl;
258
259 for (jpeg_saved_marker_ptr marker = cinfo.marker_list; marker != NULL; marker = marker->next) {
260 kdDebug(41008) << "Marker is " << marker->marker << endl;
261 if (marker->marker != (JOCTET) (JPEG_APP0 + 1) ||
262 marker->data_length < 14)
263 continue; /* Exif data is in an APP1 marker of at least 14 octets */
264
265 if (GETJOCTET (marker->data[0]) != (JOCTET) 0x45 ||
266 GETJOCTET (marker->data[1]) != (JOCTET) 0x78 ||
267 GETJOCTET (marker->data[2]) != (JOCTET) 0x69 ||
268 GETJOCTET (marker->data[3]) != (JOCTET) 0x66 ||
269 GETJOCTET (marker->data[4]) != (JOCTET) 0x00 ||
270 GETJOCTET (marker->data[5]) != (JOCTET) 0x00)
271 continue; /* no Exif header */
272 kdDebug(41008) << "Found exif information of length : "<< marker->data_length << endl;
273 KisExifIO exifIO(layer->paintDevice()->exifInfo());
274 exifIO.readExifFromMem( marker->data , marker->data_length );
275 // Interpret orientation tag
276 ExifValue v;
277 if( layer->paintDevice()->exifInfo()->getValue("Orientation", v) && v.type() == ExifValue::EXIF_TYPE_SHORT)
278 {
279 switch(v.asShort(0)) //
280 {
281 case 2:
282 layer->paintDevice()->mirrorY();
283 break;
284 case 3:
285 image()->rotate(M_PI, 0);
286 break;
287 case 4:
288 layer->paintDevice()->mirrorX();
289 break;
290 case 5:
291 image()->rotate(M_PI/2, 0);
292 layer->paintDevice()->mirrorY();
293 break;
294 case 6:
295 image()->rotate(M_PI/2, 0);
296 break;
297 case 7:
298 image()->rotate(M_PI/2, 0);
299 layer->paintDevice()->mirrorX();
300 break;
301 case 8:
302 image()->rotate(-M_PI/2 + 2*M_PI, 0);
303 break;
304 default:
305 break;
306 }
307 v.setValue(0, (TQ_UINT16)1);
308 layer->paintDevice()->exifInfo()->setValue("Orientation", v);
309 }
310 break;
311 }
312
313 // Finish decompression
314 jpeg_finish_decompress(&cinfo);
315 jpeg_destroy_decompress(&cinfo);
316 fclose(fp);
317 delete []row_pointer;
318 return KisImageBuilder_RESULT_OK;
319}
320
321
322
323KisImageBuilder_Result KisJPEGConverter::buildImage(const KURL& uri)
324{
325 if (uri.isEmpty())
326 return KisImageBuilder_RESULT_NO_URI;
327
328 if (!TDEIO::NetAccess::exists(uri, false, tqApp -> mainWidget())) {
329 return KisImageBuilder_RESULT_NOT_EXIST;
330 }
331
332 // We're not set up to handle asynchronous loading at the moment.
333 KisImageBuilder_Result result = KisImageBuilder_RESULT_FAILURE;
334 TQString tmpFile;
335
336 if (TDEIO::NetAccess::download(uri, tmpFile, tqApp -> mainWidget())) {
337 KURL uriTF;
338 uriTF.setPath( tmpFile );
339 result = decode(uriTF);
340 TDEIO::NetAccess::removeTempFile(tmpFile);
341 }
342
343 return result;
344}
345
346
347KisImageSP KisJPEGConverter::image()
348{
349 return m_img;
350}
351
352
353KisImageBuilder_Result KisJPEGConverter::buildFile(const KURL& uri, KisPaintLayerSP layer, vKisAnnotationSP_it annotationsStart, vKisAnnotationSP_it annotationsEnd, KisJPEGOptions options, KisExifInfo* exifInfo)
354{
355 if (!layer)
356 return KisImageBuilder_RESULT_INVALID_ARG;
357
358 KisImageSP img = layer -> image();
359 if (!img)
360 return KisImageBuilder_RESULT_EMPTY;
361
362 if (uri.isEmpty())
363 return KisImageBuilder_RESULT_NO_URI;
364
365 if (!uri.isLocalFile())
366 return KisImageBuilder_RESULT_NOT_LOCAL;
367 // Open file for writing
368 FILE *fp = fopen(TQFile::encodeName(uri.path()), "wb");
369 if (!fp)
370 {
371 return (KisImageBuilder_RESULT_FAILURE);
372 }
373 uint height = img->height();
374 uint width = img->width();
375 // Initialize structure
376 struct jpeg_compress_struct cinfo;
377 jpeg_create_compress(&cinfo);
378 // Initialize error output
379 struct jpeg_error_mgr jerr;
380 cinfo.err = jpeg_std_error(&jerr);
381 // Initialize output stream
382 jpeg_stdio_dest(&cinfo, fp);
383
384 cinfo.image_width = width; // image width and height, in pixels
385 cinfo.image_height = height;
386 cinfo.input_components = img->colorSpace()->nColorChannels(); // number of color channels per pixel */
387 J_COLOR_SPACE color_type = getColorTypeforColorSpace(img->colorSpace());
388 if(color_type == JCS_UNKNOWN)
389 {
390 TDEIO::del(uri);
391 return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
392 }
393 cinfo.in_color_space = color_type; // colorspace of input image
394
395
396 // Set default compression parameters
397 jpeg_set_defaults(&cinfo);
398 // Customize them
399 jpeg_set_quality(&cinfo, options.quality, true);
400
401 if(options.progressive)
402 {
403 jpeg_simple_progression (&cinfo);
404 }
405
406 // Start compression
407 jpeg_start_compress(&cinfo, true);
408 // Save exif information if any available
409 if(exifInfo)
410 {
411 kdDebug(41008) << "Trying to save exif information" << endl;
412 KisExifIO exifIO(exifInfo);
413 unsigned char* exif_data;
414 unsigned int exif_size;
415 exifIO.saveExifToMem( &exif_data, &exif_size);
416 kdDebug(41008) << "Exif informations size is " << exif_size << endl;
417 if (exif_size < MAX_DATA_BYTES_IN_MARKER)
418 {
419 jpeg_write_marker(&cinfo, JPEG_APP0 + 1, exif_data, exif_size);
420 } else {
421 kdDebug(41008) << "exif informations couldn't be saved." << endl;
422 }
423 }
424
425
426 // Save annotation
427 vKisAnnotationSP_it it = annotationsStart;
428 while(it != annotationsEnd) {
429 if (!(*it) || (*it) -> type() == TQString()) {
430 kdDebug(41008) << "Warning: empty annotation" << endl;
431 ++it;
432 continue;
433 }
434
435 kdDebug(41008) << "Trying to store annotation of type " << (*it) -> type() << " of size " << (*it) -> annotation() . size() << endl;
436
437 if ((*it) -> type().startsWith("chalk_attribute:")) { // Attribute
438 // FIXME
439 kdDebug(41008) << "can't save this annotation : " << (*it) -> type() << endl;
440 } else { // Profile
441 //char* name = new char[(*it)->type().length()+1];
442 write_icc_profile(& cinfo, (uchar*)(*it)->annotation().data(), (*it)->annotation().size());
443 }
444 ++it;
445 }
446
447
448 // Write data information
449
450 JSAMPROW row_pointer = new JSAMPLE[width*cinfo.input_components];
451 int color_nb_bits = 8 * layer->paintDevice()->pixelSize() / layer->paintDevice()->nChannels();
452
453 for (; cinfo.next_scanline < height;) {
454 KisHLineIterator it = layer->paintDevice()->createHLineIterator(0, cinfo.next_scanline, width, false);
455 TQ_UINT8 *dst = row_pointer;
456 switch(color_type)
457 {
458 case JCS_GRAYSCALE:
459 if(color_nb_bits == 16)
460 {
461 while (!it.isDone()) {
462 const TQ_UINT16 *d = reinterpret_cast<const TQ_UINT16 *>(it.rawData());
463 *(dst++) = d[0] / TQ_UINT8_MAX;
464 ++it;
465 }
466 } else {
467 while (!it.isDone()) {
468 const TQ_UINT8 *d = it.rawData();
469 *(dst++) = d[0];
470 ++it;
471 }
472 }
473 break;
474 case JCS_RGB:
475 if(color_nb_bits == 16)
476 {
477 while (!it.isDone()) {
478 const TQ_UINT16 *d = reinterpret_cast<const TQ_UINT16 *>(it.rawData());
479 *(dst++) = d[2] / TQ_UINT8_MAX;
480 *(dst++) = d[1] / TQ_UINT8_MAX;
481 *(dst++) = d[0] / TQ_UINT8_MAX;
482 ++it;
483 }
484 } else {
485 while (!it.isDone()) {
486 const TQ_UINT8 *d = it.rawData();
487 *(dst++) = d[2];
488 *(dst++) = d[1];
489 *(dst++) = d[0];
490 ++it;
491 }
492 }
493 break;
494 case JCS_CMYK:
495 if(color_nb_bits == 16)
496 {
497 while (!it.isDone()) {
498 const TQ_UINT16 *d = reinterpret_cast<const TQ_UINT16 *>(it.rawData());
499 *(dst++) = TQ_UINT8_MAX - d[0] / TQ_UINT8_MAX;
500 *(dst++) = TQ_UINT8_MAX - d[1] / TQ_UINT8_MAX;
501 *(dst++) = TQ_UINT8_MAX - d[2] / TQ_UINT8_MAX;
502 *(dst++) = TQ_UINT8_MAX - d[3] / TQ_UINT8_MAX;
503 ++it;
504 }
505 } else {
506 while (!it.isDone()) {
507 const TQ_UINT8 *d = it.rawData();
508 *(dst++) = TQ_UINT8_MAX - d[0];
509 *(dst++) = TQ_UINT8_MAX - d[1];
510 *(dst++) = TQ_UINT8_MAX - d[2];
511 *(dst++) = TQ_UINT8_MAX - d[3];
512 ++it;
513 }
514 }
515 break;
516 default:
517 TDEIO::del(uri);
518 return KisImageBuilder_RESULT_UNSUPPORTED;
519 }
520 jpeg_write_scanlines(&cinfo, &row_pointer, 1);
521 }
522
523
524 // Writting is over
525 jpeg_finish_compress(&cinfo);
526 fclose(fp);
527
528 delete [] row_pointer;
529 // Free memory
530 jpeg_destroy_compress(&cinfo);
531
532 return KisImageBuilder_RESULT_OK;
533}
534
535
536void KisJPEGConverter::cancel()
537{
538 m_stop = true;
539}
540
541#include "kis_jpeg_converter.moc"
542