~ubuntu-branches/ubuntu/jaunty/beagle/jaunty-security

« back to all changes in this revision

Viewing changes to Util/F-Spot/Imaging/JpegHeader.cs

  • Committer: Bazaar Package Importer
  • Author(s): Stefan Ebner
  • Date: 2008-05-04 00:31:32 UTC
  • mfrom: (1.1.21 upstream)
  • Revision ID: james.westby@ubuntu.com-20080504003132-2tkm5o8moo5952ri
Tags: 0.3.7-2ubuntu1
 * Merge from Debian unstable. (LP: #225746) Remaining Ubuntu changes:
  - debian/control:
    + Rename ice{weasel,dove}-beagle to {mozilla,thunderbird}-beagle and
      and update the dependencies accordingly.
    + Change Maintainer to Ubuntu Mono Team.
  - debian/rules:
    + Install the mozilla-beagle and thunderbird-beagle extensions.
  - ice{dove,weasel}.dirs:
    + Renamed to {mozilla,thunderbird}-beagle.dirs.
    + Fixed paths to point to usr/lib/{firefox,thunderbird}

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2006 Novell Inc. 
 
3
 *
 
4
 *
 
5
 * Permission is hereby granted, free of charge, to any person obtaining a
 
6
 * copy of this software and associated documentation files (the "Software"),
 
7
 * to deal in the Software without restriction, including without limitation
 
8
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 
9
 * and/or sell copies of the Software, and to permit persons to whom the
 
10
 * Software is furnished to do so, subject to the following conditions:
 
11
 *
 
12
 * The above copyright notice and this permission notice shall be included in
 
13
 * all copies or substantial portions of the Software.
 
14
 *
 
15
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
16
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
17
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 
18
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 
19
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 
20
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 
21
 * DEALINGS IN THE SOFTWARE.
 
22
 *
 
23
 */
 
24
 
 
25
using System;
 
26
using System.IO;
 
27
using System.Collections;
 
28
using FSpot.Xmp;
 
29
using FSpot;
 
30
 
 
31
#if ENABLE_NUNIT
 
32
using NUnit.Framework;
 
33
#endif
 
34
 
 
35
public class JpegHeader : SemWeb.StatementSource {
 
36
        public enum JpegMarker {
 
37
                Tem = 0x01,
 
38
                Rst0 = 0xd0,  // RstN used for resync, ignore
 
39
                Rst1 = 0xd1,
 
40
                Rst2 = 0xd2,
 
41
                Rst3 = 0xd3,
 
42
                Rst4 = 0xd4,
 
43
                Rst5 = 0xd5,
 
44
                Rst6 = 0xd6,
 
45
                Rst7 = 0xd7,
 
46
                
 
47
                Sof0 = 0xc0, // SOFn Start of frame 0-1 common
 
48
                Sof1 = 0xc1,
 
49
                Sof2 = 0xc2,
 
50
                Sof3 = 0xc3,
 
51
                
 
52
                Dht = 0xc4,  // Define Huffman Table
 
53
                
 
54
                Sof5 = 0xc5,
 
55
                Sof6 = 0xc6,
 
56
                Sof7 = 0xc7,
 
57
                
 
58
                Jpg = 0xc8, // reserved
 
59
                
 
60
                Sof9 = 0xc9,
 
61
                Sof10 = 0xca,
 
62
                Sof11 = 0xcb,
 
63
                Sof12 = 0xcc,
 
64
                Sof13 = 0xcd,
 
65
                Sof14 = 0xce,
 
66
                Sof15 = 0xcf,
 
67
 
 
68
                // These tags all consist of a marker and then a length.
 
69
                
 
70
                // These are the major structure tags.
 
71
                Soi  = 0xd8,  // Start of Image
 
72
                Eoi  = 0xd9,  // End of Image
 
73
                Sos  = 0xda,  // Start of Scan
 
74
                
 
75
                Dnl = 0xdc,
 
76
                Dri = 0xdd,  // Define restart interval
 
77
                Dhp = 0xde,
 
78
                Exp = 0xdf, 
 
79
                
 
80
                Dqt = 0xdb, // Define Quantization Table
 
81
                
 
82
                // These are the app marker tags that contain the application metadata
 
83
                // in its various forms.
 
84
                App0 = 0xe0,  // AppN Markers for application data
 
85
                App1 = 0xe1,
 
86
                App2 = 0xe2,
 
87
                App3 = 0xe3,
 
88
                App4 = 0xe4,
 
89
                App5 = 0xe5,
 
90
                App6 = 0xe6,
 
91
                App7 = 0xe7,
 
92
                App8 = 0xe8,
 
93
                App9 = 0xe9,
 
94
                App10 = 0xea,
 
95
                App11 = 0xeb,
 
96
                App12 = 0xec,
 
97
                App13 = 0xed,
 
98
                App14 = 0xee,
 
99
                App15 = 0xef,
 
100
                
 
101
                Jpg0 = 0xf0,
 
102
                Jpg1 = 0xf1,
 
103
                Jpg2 = 0xf2,
 
104
                Jpg3 = 0xf3,
 
105
                Jpg4 = 0xf4,
 
106
                Jpg5 = 0xf5,
 
107
                Jpg6 = 0xf6,
 
108
                Jpg7 = 0xf7,
 
109
                Jpg8 = 0xf8,
 
110
                Jpg9 = 0xf9,
 
111
                Jpg10 = 0xfa,
 
112
                Jpg11 = 0xfb,
 
113
                Jpg12 = 0xfc,
 
114
                Jpg13 = 0xfd,
 
115
                
 
116
                Com = 0xfe // Comment
 
117
        }       
 
118
 
 
119
        private System.Collections.ArrayList marker_list = new System.Collections.ArrayList (); 
 
120
        private byte [] image_data;
 
121
 
 
122
                // False seems a safe default
 
123
                public bool Distinct {
 
124
                        get { return false; }
 
125
                }
 
126
 
 
127
 
 
128
        public class Marker {
 
129
                public JpegMarker Type;
 
130
                public byte [] Data;
 
131
                
 
132
                public Marker (JpegMarker type, byte [] data)
 
133
                {
 
134
                        this.Type = type;
 
135
                        this.Data = data;
 
136
                }
 
137
 
 
138
                public bool IsApp {
 
139
                        get {
 
140
                                return (this.Type >= JpegMarker.App0 && this.Type <= JpegMarker.App15);
 
141
                        }
 
142
                }
 
143
 
 
144
                public bool Matches (Signature sig) 
 
145
                {
 
146
                        if (Type == sig.Id) {
 
147
                                if (sig.Name == null)
 
148
                                        return true;
 
149
                                
 
150
                                byte [] name = System.Text.Encoding.ASCII.GetBytes (sig.Name);
 
151
 
 
152
                                for (int i = 0; i < name.Length; i++)
 
153
                                        if (Data [i] != name [i])
 
154
                                                return false;
 
155
                                
 
156
                                return true;
 
157
                        }
 
158
                        return false;
 
159
                }
 
160
 
 
161
                public string GetName ()
 
162
                {
 
163
                        if (!this.IsApp)
 
164
                                return null;
 
165
                        
 
166
                        int j;
 
167
                        for (j = 0; j < this.Data.Length; j++) {
 
168
                                if (this.Data [j] == 0x00)
 
169
                                        break;
 
170
                                        
 
171
                        }
 
172
                        
 
173
                        if (j > 0)
 
174
                                return System.Text.Encoding.ASCII.GetString (this.Data, 0, j);
 
175
                        else 
 
176
                                return null;
 
177
                }
 
178
                
 
179
                private static int Read (Stream stream, byte [] dest, int start, int len)
 
180
                {
 
181
                        int pos = 0;
 
182
 
 
183
                        while (pos < len) {
 
184
                                int read = stream.Read (dest, pos + start, len - pos);
 
185
                                if (read <= 0)
 
186
                                        break;
 
187
 
 
188
                                pos += read;
 
189
                        }
 
190
                        return pos;
 
191
                }
 
192
 
 
193
                public static Marker Load (Stream stream) {
 
194
                        byte [] raw = new byte [2];
 
195
                        ushort length;
 
196
                       
 
197
                        if (stream.Length - stream.Position < 2)
 
198
                                return null;
 
199
 
 
200
                        // FIXME there is a potential loop here.
 
201
                        
 
202
                        int read = Read (stream, raw, 0, 2);
 
203
                        if (read < 2 || raw [0] != 0xff)
 
204
                                throw new System.Exception (System.String.Format ("Invalid marker found {0}", raw [0]));
 
205
                        
 
206
                        JpegMarker id = (JpegMarker) raw [1];
 
207
                        switch (id) {
 
208
                        case JpegMarker.Soi:
 
209
                        case JpegMarker.Eoi:
 
210
                        case JpegMarker.Rst0:
 
211
                        case JpegMarker.Rst1:
 
212
                        case JpegMarker.Rst2:
 
213
                        case JpegMarker.Rst3:
 
214
                        case JpegMarker.Rst4:
 
215
                        case JpegMarker.Rst5:
 
216
                        case JpegMarker.Rst6:
 
217
                        case JpegMarker.Rst7:
 
218
                        case JpegMarker.Tem: 
 
219
                        case (JpegMarker) 0:
 
220
                                return new Marker (id, null);
 
221
                        default:
 
222
                                Read (stream, raw, 0, 2);
 
223
                                length = FSpot.BitConverter.ToUInt16 (raw, 0, false);
 
224
                                
 
225
                                byte [] data = new byte [length - 2];
 
226
                                Read (stream, data, 0, data.Length);
 
227
                                return new Marker (id, data);
 
228
                        }
 
229
                        
 
230
                }
 
231
 
 
232
                public void Save (System.IO.Stream stream) {
 
233
                        /* 
 
234
                         * It is possible we should just base this choice off the existance
 
235
                         * of this.Data, but I'm not sure so I'll do it this way for now
 
236
                         */
 
237
                        
 
238
                        switch (this.Type) {
 
239
                        case JpegMarker.Soi:
 
240
                        case JpegMarker.Eoi:
 
241
                                stream.WriteByte (0xff);                                
 
242
                                stream.WriteByte ((byte)this.Type);
 
243
                                break;
 
244
                        default:
 
245
                                stream.WriteByte (0xff);                                
 
246
                                stream.WriteByte ((byte)this.Type);
 
247
                                ushort length = (ushort)(this.Data.Length + 2);
 
248
                                
 
249
                                byte [] len = FSpot.BitConverter.GetBytes (length, false);
 
250
                                stream.Write (len, 0, len.Length);
 
251
 
 
252
                                //workaround for mono bug: http://bugzilla.ximian.com/show_bug.cgi?id=82836
 
253
                                if (this.Data.Length > 0)
 
254
                                        stream.Write (this.Data, 0, this.Data.Length);
 
255
                                break;
 
256
                        }
 
257
                }
 
258
        }
 
259
 
 
260
        public static Signature JfifSignature = new Signature (JpegMarker.App0, "JFIF\0");
 
261
        public static Signature ComSignature = new Signature (JpegMarker.Com, null);
 
262
        public static Signature JfxxSignature = new Signature (JpegMarker.App0, "JFXX\0");
 
263
        public static Signature XmpSignature = new Signature (JpegMarker.App1, "http://ns.adobe.com/xap/1.0/\0");
 
264
        public static Signature ExifSignature = new Signature (JpegMarker.App1, "Exif\0\0");
 
265
        public static Signature IccProfileSignature = new Signature (JpegMarker.App2, "ICC_PROFILE\0");
 
266
        public static Signature PhotoshopSignature = new Signature (JpegMarker.App13, "Photoshop 3.0\0");
 
267
        public static Signature QuantizationSignature = new Signature (JpegMarker.Dqt, null);
 
268
 
 
269
        public class Signature {
 
270
                public JpegMarker Id;
 
271
                public string Name;
 
272
 
 
273
                public Signature (JpegMarker marker, string name)
 
274
                {
 
275
                        Id = marker;
 
276
                        Name = name;
 
277
                }
 
278
 
 
279
                public int WriteName (Stream stream)
 
280
                {
 
281
                        byte [] sig = System.Text.Encoding.ASCII.GetBytes (Name);
 
282
                        stream.Write (sig, 0, sig.Length);
 
283
                        return sig.Length;
 
284
                }
 
285
        }       
 
286
 
 
287
        public Marker FindMarker (Signature sig)
 
288
        {
 
289
                foreach (Marker m in Markers) {
 
290
                        if (m.Matches (sig))
 
291
                                return m;
 
292
                }
 
293
 
 
294
                return null;
 
295
        }
 
296
 
 
297
 
 
298
        public Marker FindMarker (JpegMarker id, string name)
 
299
        {
 
300
                return FindMarker (new Signature (id, name));
 
301
        }
 
302
 
 
303
#if false
 
304
        public Cms.Profile GetProfile ()
 
305
        {
 
306
                Marker m = FindMarker (IccProfileSignature);
 
307
                string name = IccProfileSignature.Name;
 
308
                try {
 
309
                        if (m != null)
 
310
                                return new Cms.Profile (m.Data, name.Length, m.Data.Length - name.Length); 
 
311
                } catch (System.Exception e) {
 
312
                        //System.Console.WriteLine (e);
 
313
                }
 
314
                
 
315
                FSpot.Tiff.Header exif = GetExifHeader ();
 
316
                if (exif != null)
 
317
                        return exif.Directory.GetProfile ();
 
318
                
 
319
                return null;
 
320
        }
 
321
#endif
 
322
        public FSpot.Tiff.Header GetExifHeader ()
 
323
        {
 
324
                string name = ExifSignature.Name;
 
325
                Marker marker = FindMarker (ExifSignature);
 
326
 
 
327
                if (marker == null)
 
328
                        return null;
 
329
                
 
330
                using (System.IO.Stream exifstream = new System.IO.MemoryStream (marker.Data, name.Length, marker.Data.Length - name.Length, false)) {
 
331
                        FSpot.Tiff.Header exif = new FSpot.Tiff.Header (exifstream);
 
332
                        return exif;
 
333
                }
 
334
        }
 
335
 
 
336
        public string GetJFIFComment ()
 
337
        {
 
338
                string name = ComSignature.Name;
 
339
                Marker marker = FindMarker (ComSignature);
 
340
 
 
341
                if (marker == null)
 
342
                        return null;
 
343
 
 
344
                if (marker.Data != null && marker.Data.Length != 0)
 
345
                        return System.Text.Encoding.Default.GetString (marker.Data, 0, marker.Data.Length);
 
346
 
 
347
                return null;
 
348
        }
 
349
 
 
350
        public XmpFile GetXmp ()
 
351
        {
 
352
                string name = XmpSignature.Name;
 
353
                Marker marker = FindMarker (XmpSignature);
 
354
                if (marker != null) {
 
355
                        int len = name.Length;
 
356
                        //System.Console.WriteLine (System.Text.Encoding.ASCII.GetString (marker.Data, len, 
 
357
                        //                                                              marker.Data.Length - len));
 
358
                        using (System.IO.Stream xmpstream = new System.IO.MemoryStream (marker.Data, len, 
 
359
                                                                                        marker.Data.Length - len, false)) {
 
360
                        
 
361
                                XmpFile xmp = new XmpFile (xmpstream);                                  
 
362
                                return xmp;
 
363
                        }
 
364
                }
 
365
                return null;
 
366
        }
 
367
 
 
368
        public void Select (SemWeb.StatementSink sink)
 
369
        {
 
370
                FSpot.Tiff.Header exif = GetExifHeader ();
 
371
                if (exif != null)
 
372
                        exif.Select (sink);
 
373
                
 
374
                XmpFile xmp = GetXmp ();
 
375
                if (xmp != null)
 
376
                        xmp.Select (sink);
 
377
                
 
378
                string name = PhotoshopSignature.Name;
 
379
                JpegHeader.Marker marker = FindMarker (PhotoshopSignature);
 
380
                if (marker != null) {
 
381
                        int len = name.Length;
 
382
                        using (System.IO.Stream bimstream = new System.IO.MemoryStream (marker.Data, len, marker.Data.Length - len, false)) {
 
383
                                FSpot.Bim.BimFile bim = new FSpot.Bim.BimFile (bimstream);
 
384
                                bim.Select (sink);
 
385
                        }
 
386
                }
 
387
        }
 
388
 
 
389
        public FSpot.Iptc.IptcFile GetIptc ()
 
390
        {
 
391
                string name = PhotoshopSignature.Name;
 
392
                JpegHeader.Marker marker = FindMarker (PhotoshopSignature);
 
393
                if (marker != null) {
 
394
                        int len = name.Length;
 
395
                        using (System.IO.Stream bimstream = new System.IO.MemoryStream (marker.Data, len,
 
396
                                                                                        marker.Data.Length - len, false)) {
 
397
 
 
398
                                FSpot.Bim.BimFile bim;
 
399
                                try {
 
400
                                        bim = new FSpot.Bim.BimFile (bimstream);
 
401
                                } catch {
 
402
                                        // Bim entry with marker "PHUT" is not handled by Bim.cs
 
403
                                        return null;
 
404
                                }
 
405
 
 
406
                                // FIXME: What about EntryType.XMP ?
 
407
                                FSpot.Bim.Entry iptc_entry = bim.FindEntry (FSpot.Bim.EntryType.IPTCNAA);
 
408
                                if (iptc_entry == null)
 
409
                                        return null;
 
410
 
 
411
                                using (System.IO.Stream iptcstream = new System.IO.MemoryStream (iptc_entry.Data)) {
 
412
                                        FSpot.Iptc.IptcFile iptc = new FSpot.Iptc.IptcFile (iptcstream);
 
413
                                        return iptc;
 
414
                                }
 
415
                        }
 
416
                }
 
417
                return null;
 
418
        }
 
419
 
 
420
        public Exif.ExifData Exif {
 
421
                get {
 
422
                        Marker m = FindMarker (ExifSignature);
 
423
 
 
424
                        if (m  == null)
 
425
                                return null;
 
426
 
 
427
                        return new Exif.ExifData (m.Data);
 
428
                }
 
429
        }
 
430
 
 
431
        public void Replace (Signature sig, Marker data)
 
432
        {
 
433
                bool added = false;
 
434
                for (int i = 1; i < Markers.Count; i++) {
 
435
                        Marker m = (Marker) Markers [i];
 
436
                        
 
437
                        if (m.Matches (sig)) {
 
438
                                
 
439
                                if (!added) {
 
440
                                        Markers [i] = data;
 
441
                                        added = true;
 
442
                                } else
 
443
                                        Markers.RemoveAt (i--);
 
444
                                
 
445
                        } else if (!m.IsApp || m.Type > sig.Id) {
 
446
                                if (!added) {
 
447
                                        Markers.Insert (i, data); 
 
448
                                        added = true;
 
449
                                }
 
450
                        }
 
451
                }
 
452
 
 
453
                if (!added)
 
454
                        throw new System.Exception (String.Format ("unable to replace {0} marker", sig.Name));
 
455
        }
 
456
 
 
457
        public void SetExif (Exif.ExifData value)
 
458
        {
 
459
                // Console.WriteLine ("before save");
 
460
                byte [] raw_data = value.Save ();
 
461
                // Console.WriteLine ("saved");
 
462
                Marker exif = new Marker (ExifSignature.Id, raw_data);
 
463
                // Console.WriteLine ("new");
 
464
                Replace (ExifSignature, exif);
 
465
                // Console.WriteLine ("replaced");
 
466
        }       
 
467
        
 
468
        public void SetXmp (XmpFile xmp)
 
469
        {
 
470
                using (MemoryStream stream = new MemoryStream ()) {
 
471
                        
 
472
                        XmpSignature.WriteName (stream);
 
473
                        xmp.Save (stream);
 
474
                        
 
475
                        Marker xmp_marker = new Marker (XmpSignature.Id, stream.ToArray ());
 
476
                        Replace (XmpSignature, xmp_marker);
 
477
                }
 
478
        }
 
479
        
 
480
        public System.Collections.ArrayList Markers {
 
481
                get {
 
482
                        return marker_list;
 
483
                }
 
484
        }
 
485
 
 
486
        public void Save (System.IO.Stream stream)
 
487
        {
 
488
                foreach (Marker marker in marker_list) {
 
489
                        // System.Console.WriteLine ("saving marker {0} {1}", marker.Type, 
 
490
                        //                        (marker.Data != null) ? marker.Data.Length .ToString (): "(null)");
 
491
                        marker.Save (stream);
 
492
                        if (marker.Type == JpegMarker.Sos)
 
493
                                stream.Write (ImageData, 0, ImageData.Length);
 
494
                }
 
495
        }
 
496
 
 
497
        public JpegHeader (System.IO.Stream stream)
 
498
        {
 
499
                Load (stream, false);
 
500
        }
 
501
 
 
502
        public JpegHeader (System.IO.Stream stream, bool metadata_only)
 
503
        {
 
504
                try {
 
505
                        Load (stream, metadata_only);
 
506
                } catch (System.Exception e) {
 
507
                        Console.WriteLine ("Exeption while reading jpeg headers");
 
508
                        Console.WriteLine(e);
 
509
                }
 
510
        }
 
511
 
 
512
        private void Load (System.IO.Stream stream, bool metadata_only) 
 
513
        {
 
514
                marker_list.Clear ();
 
515
                image_data = null;
 
516
                bool at_image = false;
 
517
 
 
518
                Marker marker = Marker.Load (stream);
 
519
                if (marker == null || marker.Type != JpegMarker.Soi)
 
520
                        throw new System.Exception ("This doesn't appear to be a jpeg stream");
 
521
                
 
522
                this.Markers.Add (marker);
 
523
                while (!at_image) {
 
524
                        marker = Marker.Load (stream);
 
525
 
 
526
                        if (marker == null)
 
527
                                break;
 
528
 
 
529
                        // System.Console.WriteLine ("loaded marker {0} length {1}", marker.Type, marker.Data.Length);
 
530
 
 
531
                        this.Markers.Add (marker);
 
532
                        
 
533
                        if (marker.Type == JpegMarker.Sos) {
 
534
                                at_image = true;
 
535
 
 
536
                                if (metadata_only) {
 
537
                                        // System.Console.WriteLine ("read = {0}", stream.Position);
 
538
                                        return;
 
539
                                }
 
540
                        }
 
541
                }
 
542
 
 
543
                long image_data_length = stream.Length - stream.Position;
 
544
                this.image_data = new byte [image_data_length];
 
545
 
 
546
                if (stream.Read (image_data, 0, (int)image_data_length) != image_data_length)
 
547
                        throw new System.Exception ("truncated image data or something");
 
548
        }
 
549
 
 
550
        static int [] StandardLuminanceQuantization = new int [] {
 
551
                16,  11,  12,  14,  12,  10,  16,  14,
 
552
                13,  14,  18,  17,  16,  19,  24,  40,
 
553
                26,  24,  22,  22,  24,  49,  35,  37,
 
554
                29,  40,  58,  51,  61,  60,  57,  51,
 
555
                56,  55,  64,  72,  92,  78,  64,  68,
 
556
                87,  69,  55,  56,  80, 109,  81,  87,
 
557
                95,  98, 103, 104, 103,  62,  77, 113,
 
558
                121, 112, 100, 120,  92, 101, 103,  99
 
559
        };
 
560
 
 
561
        static int [] StandardChrominanceQuantization = new int [] {
 
562
                17,  18,  18,  24,  21,  24,  47,  26,
 
563
                26,  47,  99,  66,  56,  66,  99,  99,
 
564
                99,  99,  99,  99,  99,  99,  99,  99,
 
565
                99,  99,  99,  99,  99,  99,  99,  99,
 
566
                99,  99,  99,  99,  99,  99,  99,  99,
 
567
                99,  99,  99,  99,  99,  99,  99,  99,
 
568
                99,  99,  99,  99,  99,  99,  99,  99,
 
569
                99,  99,  99,  99,  99,  99,  99,  99
 
570
        };
 
571
        
 
572
        /* 
 
573
         * GuessQuality is taken from the jpegdump utility
 
574
         * Copyright (c) 1992 Handmade Software, Inc.
 
575
         * by Allan N. Hessenflow licenced as GPL with the authors
 
576
         * permission.  Many Thanks.
 
577
         */
 
578
        public int GuessQuality ()
 
579
        {
 
580
                Marker dqt = FindMarker (QuantizationSignature);
 
581
                int quality = 0;
 
582
                int position = 0;
 
583
 
 
584
                while (position < dqt.Data.Length) {
 
585
                        int tableindex;
 
586
                        int [] table = null;
 
587
                        double cumsf = 0.0;
 
588
                        double cumsf2 = 0.0;
 
589
                        bool allones = true;
 
590
                        int row, col;
 
591
                        
 
592
                        tableindex = dqt.Data [position ++];
 
593
 
 
594
                        switch (tableindex & 0x0f) {
 
595
                        case 0:
 
596
                                table = StandardLuminanceQuantization;
 
597
                                break;
 
598
                        case 1:
 
599
                                table = StandardChrominanceQuantization;
 
600
                                break;
 
601
                        default:
 
602
                                table = null;
 
603
                                break;
 
604
                        }
 
605
 
 
606
                        for (row=0; row<8; row++) {
 
607
                                for (col=0; col<8; col++) {
 
608
                                        uint val;
 
609
                                        
 
610
                                        if ((tableindex >> 4) > 0) {
 
611
                                                val = FSpot.BitConverter.ToUInt16 (dqt.Data, position, false);
 
612
                                                position += 2;
 
613
                                        } else
 
614
                                                val = (uint) dqt.Data [position ++];
 
615
 
 
616
                                        if (table != null) {
 
617
                                                double x;
 
618
 
 
619
                                                /* scaling factor in percent */
 
620
                                                x = 100.0 * (double)val / (double)table [row*8+col];
 
621
                                                cumsf += x;
 
622
                                                cumsf2 += x * x;
 
623
 
 
624
                                                /* separate check for all-ones table (Q 100) */
 
625
                                                if (val != 1) 
 
626
                                                        allones = false;
 
627
                                        }
 
628
                                }
 
629
                        }
 
630
 
 
631
                        if (table != null) {
 
632
                                double local_quality;
 
633
                                
 
634
                                cumsf /= 64.0;  /* mean scale factor */
 
635
                                cumsf2 /= 64.0;
 
636
 
 
637
                                //double variance;
 
638
                                //variance = cumsf2 - (cumsf * cumsf);
 
639
 
 
640
                                if (allones) /* special case for all-ones table */
 
641
                                        local_quality = 100.0;
 
642
                                else if (cumsf <= 100.0)
 
643
                                        local_quality = (200.0 - cumsf) / 2.0;
 
644
                                else
 
645
                                        local_quality = 5000.0 / cumsf;
 
646
                                
 
647
                                quality = Math.Max (quality, (int)local_quality);
 
648
                        }
 
649
                }
 
650
                return quality;
 
651
        }
 
652
        
 
653
        public byte [] ImageData {
 
654
                get {
 
655
                        return image_data;
 
656
                }
 
657
        }
 
658
 
 
659
#if ENABLE_NUNIT
 
660
        [TestFixture]
 
661
        public class Tests {
 
662
                int quality =  75;
 
663
 
 
664
                public string CreateFile ()
 
665
                {
 
666
                        Gdk.Pixbuf test = new Gdk.Pixbuf (null, "f-spot-32.png");
 
667
                        string path = FSpot.ImageFile.TempPath ("joe.jpg");
 
668
                        string desc = "\x00a9 Novell Inc.";
 
669
                        PixbufOrientation orient = PixbufOrientation.TopRight;
 
670
 
 
671
                        PixbufUtils.SaveJpeg (test, path, quality, new Exif.ExifData ());
 
672
                        FSpot.JpegFile jimg = new FSpot.JpegFile (path);
 
673
                        jimg.SetDescription (desc);
 
674
                        jimg.SetOrientation (orient);
 
675
                        jimg.SaveMetaData (path);
 
676
                        
 
677
                        return path;
 
678
                }
 
679
 
 
680
                [Test]
 
681
                public void Load ()
 
682
                {
 
683
                        string path = CreateFile ();
 
684
 
 
685
                        using (Stream stream = File.OpenRead (path)) {
 
686
                                JpegHeader jhead = new JpegHeader (stream);
 
687
 
 
688
                                Assert.AreEqual (((Marker)jhead.Markers [0]).Type, JpegMarker.Soi);
 
689
                                Assert.AreEqual (((Marker)jhead.Markers [1]).GetName (), "JFIF");
 
690
                                Assert.AreEqual (((Marker)jhead.Markers [1]).Type, JpegMarker.App0);
 
691
                                Assert.AreEqual (((Marker)jhead.Markers [2]).GetName (), "Exif");
 
692
                                Assert.AreEqual (((Marker)jhead.Markers [2]).Type, JpegMarker.App1);
 
693
 
 
694
                                // NOTE the currently we don't store the Eoi as the last marker
 
695
                                Assert.AreEqual (((Marker)jhead.Markers [jhead.Markers.Count -1]).Type, JpegMarker.Sos);
 
696
 
 
697
                                // NOTE this is kind of sill but it might help
 
698
                                Assert.IsTrue (Math.Abs (jhead.GuessQuality () - quality) <= 1);
 
699
 
 
700
                                Assert.IsNotNull (jhead.GetExifHeader ());
 
701
                        }
 
702
 
 
703
                        File.Delete (path);
 
704
                }
 
705
 
 
706
                [Test]
 
707
                public void Save ()
 
708
                {
 
709
                        string in_path = CreateFile ();
 
710
                        string out_path = ImageFile.TempPath ("output.jpg");
 
711
                        JpegHeader source;
 
712
                        JpegHeader dest;
 
713
 
 
714
                        using (Stream orig = File.OpenRead (in_path)) {
 
715
                                source = new JpegHeader (orig);
 
716
                                
 
717
                                using (Stream output = File.OpenWrite (out_path)) {
 
718
                                        source.Save (output);
 
719
                                }
 
720
 
 
721
                                using (Stream result = File.OpenRead (out_path)) {
 
722
                                        dest = new JpegHeader (result);
 
723
                                        
 
724
                                        Assert.AreEqual (source.Markers.Count, dest.Markers.Count);
 
725
                                        Assert.AreEqual (source.GuessQuality (), dest.GuessQuality ());
 
726
                                        Assert.AreEqual (orig.Length, result.Length);
 
727
                                        for (int i = 0; i < source.Markers.Count; i++) {
 
728
                                                Marker d = (Marker) dest.Markers [i];
 
729
                                                Marker s = (Marker) source.Markers [i];
 
730
 
 
731
                                                Assert.AreEqual (d.Type, s.Type);
 
732
                                                Assert.AreEqual (d.GetName (), s.GetName ());
 
733
 
 
734
                                                if (d.Data != null) {
 
735
                                                        Assert.AreEqual (d.Data.Length, s.Data.Length);
 
736
                                                
 
737
                                                        for (int j = 0; j < d.Data.Length; j++) {
 
738
                                                                Assert.AreEqual (d.Data [j], s.Data [j]);
 
739
                                                        }
 
740
                                                } else {
 
741
                                                        Assert.AreEqual (d.Data, s.Data);
 
742
                                                }
 
743
                                        }
 
744
                                }
 
745
                        }
 
746
 
 
747
                        File.Delete (in_path);
 
748
                        File.Delete (out_path);
 
749
                }
 
750
        }
 
751
#endif
 
752
 
 
753
#if false
 
754
        public static int Main (string [] args)
 
755
        {
 
756
                JpegHeader data = new JpegHeader (args [0]);
 
757
                byte [] value = data.GetRawXmp ();
 
758
 
 
759
                if (value != null) {
 
760
                        string xml = System.Text.Encoding.UTF8.GetString (value, 29, value.Length - 29);
 
761
                        //System.Console.WriteLine (xml);
 
762
                }
 
763
                
 
764
                value = data.GetRaw ("ICC_PROFILE");
 
765
                if (value != null) {
 
766
                        System.IO.FileStream stream = new System.IO.FileStream ("profile.icc", System.IO.FileMode.Create);
 
767
                        stream.Write (value, 12, value.Length - 12);
 
768
                        stream.Close ();
 
769
                }
 
770
 
 
771
                value = data.GetRawExif ();
 
772
                
 
773
                
 
774
                //System.IO.Stream ostream = System.IO.File.Open ("/home/lewing/test.jpg", System.IO.FileMode.OpenOrCreate);
 
775
                //data.Save (ostream);
 
776
                //ostream.Position = 0;
 
777
                //data = new JpegHeader (ostream);
 
778
 
 
779
                return 0;
 
780
        }
 
781
#endif
 
782
}