From 3bf47c2bac5b39083c8da726e3697df905ea605527136bfda8cb6480e41fb75a Mon Sep 17 00:00:00 2001 From: OBS User unknown Date: Mon, 15 Jan 2007 23:08:44 +0000 Subject: [PATCH] OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/dcraw?expand=0&rev=1 --- .gitattributes | 23 + .gitignore | 1 + README | 612 +++++ README.SuSE | 3 + badpixels | 34 + dcraw.1 | 204 ++ dcraw.c | 7111 ++++++++++++++++++++++++++++++++++++++++++++++++ dcraw.changes | 104 + dcraw.spec | 138 + dcwrap | 27 + fujiturn.c | 102 + parse.c | 991 +++++++ ready | 0 update_dcraw | 20 + 14 files changed, 9370 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 README create mode 100644 README.SuSE create mode 100644 badpixels create mode 100644 dcraw.1 create mode 100644 dcraw.c create mode 100644 dcraw.changes create mode 100644 dcraw.spec create mode 100644 dcwrap create mode 100644 fujiturn.c create mode 100644 parse.c create mode 100644 ready create mode 100644 update_dcraw diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9b03811 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,23 @@ +## Default LFS +*.7z filter=lfs diff=lfs merge=lfs -text +*.bsp filter=lfs diff=lfs merge=lfs -text +*.bz2 filter=lfs diff=lfs merge=lfs -text +*.gem filter=lfs diff=lfs merge=lfs -text +*.gz filter=lfs diff=lfs merge=lfs -text +*.jar filter=lfs diff=lfs merge=lfs -text +*.lz filter=lfs diff=lfs merge=lfs -text +*.lzma filter=lfs diff=lfs merge=lfs -text +*.obscpio filter=lfs diff=lfs merge=lfs -text +*.oxt filter=lfs diff=lfs merge=lfs -text +*.pdf filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text +*.rpm filter=lfs diff=lfs merge=lfs -text +*.tbz filter=lfs diff=lfs merge=lfs -text +*.tbz2 filter=lfs diff=lfs merge=lfs -text +*.tgz filter=lfs diff=lfs merge=lfs -text +*.ttf filter=lfs diff=lfs merge=lfs -text +*.txz filter=lfs diff=lfs merge=lfs -text +*.whl filter=lfs diff=lfs merge=lfs -text +*.xz filter=lfs diff=lfs merge=lfs -text +*.zip filter=lfs diff=lfs merge=lfs -text +*.zst filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..57affb6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.osc diff --git a/README b/README new file mode 100644 index 0000000..db1bee6 --- /dev/null +++ b/README @@ -0,0 +1,612 @@ +http://www.cybercom.net/~dcoffin/dcraw/: + + [1]Back to Dave Coffin's Home Page + + [2]en Esperanto + + Welcome! If you are wondering how to connect your digital camera and + download images to a Linux PC, go to the [3]gPhoto homepage. My + software is for processing those images after downloading them. + + If you're downloading JPEG files, you don't need my software at all. + The image has already been processed inside the camera. Almost all + digital cameras made since 1997 produce JPEG images, so why would you + want to do it any other way? + + Well, despite the convenience and ubiquity of JPEG, there are some + disadvantages. JPEG is a lossy format -- to fit a big image into a + small file, a lot of information is thrown away. That's why midrange + and high-end digital cameras offer an alternative: Raw, unprocessed + CCD data files, for which the camera manufacturer provides special + decoding software. + + Of course this software is for Windows and Macintosh only, with no + source code. So it's useless to users of other operating systems, + programmers hoping to design a better interpolation algorithm, and + historians not yet born in an era when the only Windows machines will + be in museums. + + So here is my mission: Write and maintain an ANSI C program that + decodes any raw image from any digital camera on any computer running + any operating system. + + That program is called [4]dcraw (pronounced "dee-see-raw"), and it's + become a standard tool within and without the Open Source world. It's + small (about 7000 lines), portable (standard C libraries only), free + (both "gratis" and "libre"), and when used skillfully, produces + [5]better quality [6]output than the tools provided by the camera + vendor. + + [7]Here's my resume. I do freelance consulting related to dcraw, and + I'm also available for full-time software work in the Northeast USA. + + I can be reached by sending e-mail to cybercom dot net with the + username "dcoffin". + + My Code + + Unless otherwise noted in the source code, these programs are free for + all uses, although I would like to receive credit for them. Donations + are welcome too, if you're making money from my code. + + Note to Linux distributors: The only executable files that should be + installed by a dcraw package are "dcraw", "clean_crw", and maybe + "fuji_green", "fujiturn", and "fujiturn16". My shell scripts are + dangerous and should only be installed in a "doc" directory without + execute permission. + * [8]dcraw.c -- decodes raw photos, extracts thumbnails, and + displays metadata + Supports 231 cameras at last count. Compile with "gcc -o dcraw -O4 + dcraw.c -lm -ljpeg -llcms" or "gcc -o dcraw -O4 dcraw.c -lm + -DNO_JPEG -DNO_LCMS". Run with no arguments to see a usage + message. Don't complain that 16-bit output is too dark -- read the + FAQ! + * [9]rawphoto.c -- basic plugin for GIMP 1.2 & 2.0 + After installing "dcraw", do "gimptool --install rawphoto.c". My + plugin provides a simple dialog box for loading raw files into the + Gimp. [10]Udi Fuchs and [11]Joseph Heled have written much nicer + plugins, with live preview, histograms, and color curves. + * [12].badpixels -- my camera's "hot pixels" + This file tells dcraw which pixels have died and when, so that it + can interpolate around them. + * [13]dcraw.1 -- UNIX manpage for dcraw + Contains details and helpful hints not found elsewhere. + * [14]dcraw.c,v -- complete unabridged RCS file + This file contains the entire history of dcraw.c since its + conception on February 23, 1997. If you don't have the RCS + toolkit, [15]download it here. + * [16]parse.c -- read image data structures + This program displays CIFF and TIFF data structures in a very + cryptic format. + * [17]clean_crw.c -- clean Canon CRW files + Recovered or undeleted CRW files often have junk appended to them + that makes them unreadable. This program safely cleans CRW files. + * [18]fujiturn.c -- rotate Fuji Super CCD images + An alternative to dcraw's built-in Fuji rotation. + * [19]fuji_green.c -- convert Fuji green pixels to PGM + A side benefit of the Fuji Super CCD design is that its green + pixels make nice greyscale images. + + For hackers only: + * [20]decompress.c is a simple reference decompressor for CRW files. + * [21]sony_clear.c decrypts SRF files from the Sony DSC-F828. + + Other Raw Photo Decoders + + Dcraw has made it far easier for developers to support a wide range of + digital cameras in their applications. They can call dcraw from a + graphical interface, paste pieces of dcraw.c into their code, or just + use dcraw.c as the documentation that camera makers refuse to provide: + * [22]ACDSee + * [23]Adobe Photoshop + * [24]BR's PhotoArchiver by Baard Riiber + * [25]BreezeBrowser by Chris Breeze + * [26]Conceiva Lightbox + * [27]cPicture by Jürgen Eidt + * [28]Cumulus by Canto + * [29]dcRAW-X by Bryan Chang + * [30]DCRawUI by Sune Trudslev + * [31]Directory Opus Plugin by Leo Davidson(with C++ source code) + * [32]DeepSkyStacker by Luc Coiffier + * [33]dpMagic by Mikhail Stolpner + * [34]EasyRaw Studio + * [35]GraphicConverter by Thorsten Lemke + * [36]GVBox from JCO Consulting + * [37]ImageLab from Aragon System + * [38]IrfanView by Irfan Skiljan + * [39]IRIS image processor for astronomers + * [40]KA Photoservice + * [41]Lightbox by Josh Anon + * [42]LightZone by Anton Kast + * [43]Photo Companion by Jeff Moore + * [44]Photo Jockey by Davie Lee Reed who also wrote a [45]dcraw + interface for Delphi programmers. + * [46]Photo Organizer by Balint Kis + * [47]PhotoReviewer by Ben Haller + * [48]Picasa from Google + * [49]Picture Arena by Felix Schwarz + * [50]PixInsight by Pleiades Software + * [51]PiXPO by How2Share Technologies + * [52]PolyView by Polybytes + * [53]PowerShovel-II by Luc Minnebo + * [54]RAW Developer by Iridient Digital + * [55]Raw Magick + * [56]RawConvert by Jason Swain + * [57]RawDrop by Frank Siegert + * [58]RawShooter from pixmantec + * [59]RawView by Jari Savolainen + * [60]Serif PhotoPlus, PanoramaPlus, and AlbumPlus + * [61]SharpRaw by Duane DeSieno + * [62]SilverFast DCPro by LaserSoft Imaging + * [63]StudioLine Photo by H&M Software + * [64]ViewIt by Zdzislaw Losvik + * [65]Viewer n5 by Dmitry Fedorov + * [66]VueScan by Ed Hamrick + + Frequently Asked Questions + + I don't have a C compiler. Could you send me an executable? + No, but Francisco Montilla provides Mac OS and Windows + executables [67]on his website. And Benjamin Lebsanft has + volunteered to maintain [68]Windows executables optimized for + specific CPUs. + + If you're familiar with the DOS command line but don't know C, + you can install this [69]free C compiler for Windows and + compile dcraw.c quite easily. + + How can I read the EXIF data (shutter speed, aperture, etc.)? + [70]Phil Harvey's ExifTool provides a unified Perl-based EXIF + reader (and editor!) for all cameras and file formats. "dcraw + -i -v" is much faster, but provides less information. + + How can I read NEF files from Nikon scanners? + Dcraw only supports cameras. Try [71]this simple program for + scanners. + + How can I read Nikon Dust Off images (NDF files)? + [72]Use this program. + + Do you have any specifications describing raw photo formats? + Yes, but they tend to omit important details, like how to + decompress the raw image or decrypt private metadata. See the + [73]TIFF spec, the [74]TIFF/EP spec, the [75]Adobe DNG spec, + the [76]CIFF (CRW) spec, and the [77]X3F spec. + + I'm designing a digital camera. How do I convert its raw photos into + something that dcraw and Adobe Photoshop can open? + Download [78]LibTIFF v3.8.0 and apply [79]this patch. Then use + [80]this C program as a template for converting your photos to + valid [81]Adobe DNG files. + + I shot a raw photo with no light. Why does it appear all noisy, when + it should be solid black? + No matter how dark an image is, dcraw's auto-exposure stretches + it so that one percent of its pixels appear white. The "-b" + option adjusts this behavior, while "-4" (16-bit output) avoids + it entirely. + + I bracket plus/minus two stops, but all five shots look almost the + same in dcraw. Why? + See the previous question. + + Why is 16-bit output dark / unreadable? + If you want pretty pictures straight out of dcraw, stay with + 8-bit output. 16-bit linear output is the best raw material for + professional image editors such as [82]Photoshop and + [83]CinePaint, but it's no good for most image viewers. + + What does the "-f" (four color RGB) option do? + If you see patterns like [84]this or [85]this in your output + images, first try "dcraw -a". If these patterns persist, use + "dcraw -f" to get rid of them. + + Could you please add an option for TIFF / FITS / PNG / BMP / JPEG + output? + In versions 8.25 and later, "dcraw -T" writes TIFF output with + metadata. To write other formats: + +dcraw -c crw_0001.crw | pnmtofits > crw_0001.fits +dcraw -c crw_0001.crw | pnmtopng > crw_0001.png +dcraw -c crw_0001.crw | ppmtobmp > crw_0001.bmp +dcraw -c crw_0001.crw | cjpeg > crw_0001.jpeg + + I used the [86]Netpbm toolkit in these examples. + [87]ImageMagick also does command-line format conversions. Both + are free. + + Why don't you implement dcraw as a library? + I have decided that dcraw shall be a command-line program + written in C, and that any further abstraction layers must be + added around this core, not inside it. + + Library code is ugly because it cannot use global variables. + Libraries are more difficult to modify, build, install, and + test than standalone programs, and so are inappropriate for + file formats that change every day. + + There's a simpler way to make dcraw modular and thread-safe: + Run it as a separate process. Eric Raymond [88]explains this + technique here. + + Why are there false colors along edges within the image? + Because of interpolation. This is a hard problem, easily + defined: + + 1. Take a three-color RGB image. At each pixel, set two color + values to zero. + 2. Reconstruct the original three-color image as best you can + from the remaining one color per pixel. + + Dcraw currently gives a choice of three methods: Bilinear, + Variable Number of Gradients (VNG), and Adaptive + Homogeneity-Directed (AHD). + + [89]The Foveon X3 Capture chip requires a different kind of + interpolation. Unlike CCD arrays, it captures three colors at + every pixel location. But the colors are not well separated, so + the raw data looks very gray. Much processing is needed to + enhance color while suppressing noise. + + How do I get my camera to take raw photos? + For some Nikon Coolpix cameras, you need to enable a + [90]special "DIAG RAW" mode. + For Casio cameras, see [91]Maurice Delaney's website or read + [92]this discussion on dpreview. + For the Minolta DiMAGE G400, G500, G530, or G600, go [93]here + (in Russian) or [94]here (in English). + For the Minolta DiMAGE Z2 and Nikon Coolpix 2100/3100/3700, + [95]go here. + For SMaL cameras, see the [96]camerahacking Forum. + + For other cameras, refer to the User's Manual. + + Does dcraw work with my camera? + Most likely, yes. If your camera is not on the list below, try + dcraw anyway. If it doesn't work, post a raw image to a website + and e-mail me the URL. If you don't have a website, use + [97]Dropload or [98]YouSendIt. + + Ideally, your sample image should show a standard white card or + color chart in direct sunlight, with other colors in the + background. + + Supported Cameras + + * Adobe Digital Negative (DNG) + * AVT F-145C + * AVT F-201C + * AVT F-510C + * AVT F-810C + * Canon PowerShot 600 + * Canon PowerShot A5 + * Canon PowerShot A5 Zoom + * Canon PowerShot A50 + * Canon PowerShot Pro70 + * Canon PowerShot Pro90 IS + * Canon PowerShot G1 + * Canon PowerShot G2 + * Canon PowerShot G3 + * Canon PowerShot G5 + * Canon PowerShot G6 + * Canon PowerShot S30 + * Canon PowerShot S40 + * Canon PowerShot S45 + * Canon PowerShot S50 + * Canon PowerShot S60 + * Canon PowerShot S70 + * Canon PowerShot Pro1 + * Canon EOS D30 + * Canon EOS D60 + * Canon EOS 5D + * Canon EOS 10D + * Canon EOS 20D + * Canon EOS 30D + * Canon EOS 300D / Digital Rebel / Kiss Digital + * Canon EOS 350D / Digital Rebel XT / Kiss Digital N + * Canon EOS 400D / Digital Rebel XTi / Kiss Digital X + * Canon EOS D2000C + * Canon EOS-1D + * Canon EOS-1DS + * Canon EOS-1D Mark II + * Canon EOS-1D Mark II N + * Canon EOS-1Ds Mark II + * Casio QV-2000UX + * Casio QV-3000EX + * Casio QV-3500EX + * Casio QV-4000 + * Casio QV-5700 + * Casio QV-R51 + * Casio QV-R61 + * Casio EX-S100 + * Casio EX-Z50 + * Casio EX-Z55 + * Casio Exlim Pro 505 + * Casio Exlim Pro 600 + * Casio Exlim Pro 700 + * Contax N Digital + * Creative PC-CAM 600 + * Epson R-D1 + * Foculus 531C + * Fuji FinePix E550 + * Fuji FinePix E900 + * Fuji FinePix F700 + * Fuji FinePix F710 + * Fuji FinePix F800 + * Fuji FinePix F810 + * Fuji FinePix S2Pro + * Fuji FinePix S3Pro + * Fuji FinePix S20Pro + * Fuji FinePix S5000 + * Fuji FinePix S5100/S5500 + * Fuji FinePix S5200/S5600 + * Fuji FinePix S7000 + * Fuji FinePix S9000/S9500 + * Imacon Ixpress 16-megapixel + * Imacon Ixpress 22-megapixel + * Imacon Ixpress 39-megapixel + * ISG 2020x1520 + * Kodak DC20 (see [99]Oliver Hartman's page) + * Kodak DC25 (see [100]Jun-ichiro Itoh's page) + * Kodak DC40 + * Kodak DC50 + * Kodak DC120 (also try [101]kdc2tiff) + * Kodak DCS315C + * Kodak DCS330C + * Kodak DCS420 + * Kodak DCS460 + * Kodak DCS460A + * Kodak DCS520C + * Kodak DCS560C + * Kodak DCS620C + * Kodak DCS620X + * Kodak DCS660C + * Kodak DCS660M + * Kodak DCS720X + * Kodak DCS760C + * Kodak DCS760M + * Kodak EOSDCS1 + * Kodak EOSDCS3B + * Kodak NC2000F + * Kodak ProBack + * Kodak PB645C + * Kodak PB645H + * Kodak PB645M + * Kodak DCS Pro 14n + * Kodak DCS Pro 14nx + * Kodak DCS Pro SLR/c + * Kodak DCS Pro SLR/n + * Kodak P850 + * Kodak P880 + * Kodak KAI-0340 + * Konica KD-400Z + * Konica KD-510Z + * Leaf Aptus 17 + * Leaf Aptus 22 + * Leaf Aptus 65 + * Leaf Aptus 75 + * Leaf Cantare + * Leaf CatchLight + * Leaf CMost + * Leaf DCB2 + * Leaf Valeo 6 + * Leaf Valeo 11 + * Leaf Valeo 17 + * Leaf Valeo 22 + * Leaf Volare + * Leica Digilux 2 + * Leica D-Lux 2 + * Logitech Fotoman Pixtura + * Micron 2010 + * Minolta RD175 + * Minolta DiMAGE 5 + * Minolta DiMAGE 7 + * Minolta DiMAGE 7i + * Minolta DiMAGE 7Hi + * Minolta DiMAGE A1 + * Minolta DiMAGE A2 + * Minolta DiMAGE A200 + * Minolta DiMAGE G400 + * Minolta DiMAGE G500 + * Minolta DiMAGE G530 + * Minolta DiMAGE G600 + * Minolta DiMAGE Z2 + * Minolta Alpha/Dynax/Maxxum 5D + * Minolta Alpha/Dynax/Maxxum 7D + * Nikon D1 + * Nikon D1H + * Nikon D1X + * Nikon D2H + * Nikon D2Hs + * Nikon D2X + * Nikon D50 + * Nikon D70 + * Nikon D70s + * Nikon D80 + * Nikon D100 + * Nikon D200 + * Nikon E700 ("DIAG RAW" hack) + * Nikon E800 ("DIAG RAW" hack) + * Nikon E880 ("DIAG RAW" hack) + * Nikon E900 ("DIAG RAW" hack) + * Nikon E950 ("DIAG RAW" hack) + * Nikon E990 ("DIAG RAW" hack) + * Nikon E995 ("DIAG RAW" hack) + * Nikon E2100 ("DIAG RAW" hack) + * Nikon E2500 ("DIAG RAW" hack) + * Nikon E3200 ("DIAG RAW" hack) + * Nikon E3700 ("DIAG RAW" hack) + * Nikon E4300 ("DIAG RAW" hack) + * Nikon E4500 ("DIAG RAW" hack) + * Nikon E5000 + * Nikon E5400 + * Nikon E5700 + * Nikon E8400 + * Nikon E8700 + * Nikon E8800 + * Olympus C3030Z + * Olympus C5050Z + * Olympus C5060WZ + * Olympus C7070WZ + * Olympus C70Z,C7000Z + * Olympus C740UZ + * Olympus C770UZ + * Olympus C8080WZ + * Olympus E-1 + * Olympus E-10 + * Olympus E-20 + * Olympus E-300 + * Olympus E-330 + * Olympus E-500 + * Olympus SP310 + * Olympus SP320 + * Olympus SP350 + * Olympus SP500UZ + * Panasonic DMC-FZ30 + * Panasonic DMC-FZ50 + * Panasonic DMC-LC1 + * Panasonic DMC-LX1 + * Panasonic DMC-LX2 + * Pentax *ist D + * Pentax *ist DL + * Pentax *ist DL2 + * Pentax *ist DS + * Pentax *ist DS2 + * Pentax K100D + * Pentax Optio S + * Pentax Optio S4 + * Pentax Optio 33WR + * Phase One LightPhase + * Phase One H 10 + * Phase One H 20 + * Phase One H 25 + * Phase One P 20 + * Phase One P 25 + * Phase One P 30 + * Phase One P 45 + * Pixelink A782 + * Polaroid x530 + * Rollei d530flex + * RoverShot 3320af + * Samsung GX-1S + * Sarnoff 4096x5440 + * Sigma SD9 + * Sigma SD10 + * Sinar 3072x2048 + * Sinar 4080x4080 + * Sinar 4080x5440 + * Sinar STI format + * SMaL Ultra-Pocket 3 + * SMaL Ultra-Pocket 4 + * SMaL Ultra-Pocket 5 + * Sony DSC-F828 + * Sony DSC-R1 + * Sony DSC-V3 + * Sony DSLR-A100 + * Sony XCD-SX910CR + * STV680 VGA + _________________________________________________________________ + +References + + 1. http://www.cybercom.net/~dcoffin/ + 2. http://www.cybercom.net/~dcoffin/dcraw/index_eo.html + 3. http://gphoto.sourceforge.net/ + 4. http://www.cybercom.net/~dcoffin/dcraw/dcraw.c + 5. http://www.insflug.org/raw/ + 6. http://www.aim-dtp.net/aim/digicam/dcraw/ + 7. http://www.cybercom.net/~dcoffin/resume.html + 8. http://www.cybercom.net/~dcoffin/dcraw/dcraw.c + 9. http://www.cybercom.net/~dcoffin/dcraw/rawphoto.c + 10. http://ufraw.sourceforge.net/ + 11. http://pages.quicksilver.net.nz/pepe/ + 12. http://www.cybercom.net/~dcoffin/dcraw/.badpixels + 13. http://www.cybercom.net/~dcoffin/dcraw/dcraw.1 + 14. http://www.cybercom.net/~dcoffin/dcraw/RCS/dcraw.c,v + 15. http://www.cs.purdue.edu/homes/trinkle/RCS/ + 16. http://www.cybercom.net/~dcoffin/dcraw/parse.c + 17. http://www.cybercom.net/~dcoffin/dcraw/clean_crw.c + 18. http://www.cybercom.net/~dcoffin/dcraw/fujiturn.c + 19. http://www.cybercom.net/~dcoffin/dcraw/fuji_green.c + 20. http://www.cybercom.net/~dcoffin/dcraw/decompress.c + 21. http://www.cybercom.net/~dcoffin/dcraw/sony_clear.c + 22. http://www.acdsystems.com/ + 23. http://www.adobe.com/products/photoshop/cameraraw.html + 24. http://www.br-software.com/ + 25. http://www.breezesys.com/ + 26. http://www.conceiva.com/ + 27. http://cpicture.net/en/t_raw.html + 28. http://www.canto.com/ + 29. http://frostyplace.com/dcraw/ + 30. http://www.tanis.dk/wiki/index.php/DCRawUI + 31. http://www.pretentiousname.com/jp2raw/ + 32. http://deepskystacker.free.fr/ + 33. http://www.dpmagic.com/ + 34. http://www.easyraw.com/ + 35. http://www.lemkesoft.com/ + 36. http://jcoconsulting.com/index.asp?Section=GVOCX + 37. http://www.aragonsystem.com/ + 38. http://www.irfanview.com/ + 39. http://www.astrosurf.com/buil/us/iris/iris.htm + 40. http://www.kaorg.com/photoservice.asp + 41. http://www.lightboxsoftware.com/ + 42. http://sonic.net/~rat/lightcrafts/ + 43. http://www.wildcape.com/ + 44. http://photojockey.com/ + 45. http://smatters.com/dcraw/ + 46. http://www.k-i-s.net/ + 47. http://www.sticksoftware.com/software/PhotoReviewer.html + 48. http://www.picasa.com/ + 49. http://www.picturearena.com/ + 50. http://pleiades-astrophoto.com/ + 51. http://www.pixpo.com/ + 52. http://www.polybytes.com/ + 53. http://lens.liteserv.com/download/powershovel2.php + 54. http://www.iridientdigital.com/ + 55. http://www.rawmagick.com/ + 56. http://www.i-graph.com/ + 57. http://www.wizards.de/rawdrop + 58. http://www.pixmantec.com/ + 59. http://www.through-the-lens.net/ + 60. http://www.serif.com/ + 61. http://www.logicaldesigns.com/ + 62. http://www.silverfast.com/ + 63. http://www.studioline.net/ + 64. http://www.hexcat.com/viewit/ + 65. http://www.dimin.net/software/viewer/ + 66. http://www.hamrick.com/ + 67. http://www.insflug.org/raw/ + 68. http://www.lebsanft.org/blog/index.php?cat=9 + 69. http://www.delorie.com/djgpp/ + 70. http://www.sno.phy.queensu.ca/~phil/exiftool/ + 71. http://www.cybercom.net/~dcoffin/dcraw/scan.c + 72. http://www.cybercom.net/~dcoffin/dcraw/read_ndf.c + 73. http://partners.adobe.com/asn/developer/PDFS/TN/TIFF6.pdf + 74. http://www.map.tu.chiba-u.ac.jp/IEC/100/TA2/recdoc/N4378.pdf + 75. http://www.adobe.com/products/dng/pdfs/dng_spec.pdf + 76. http://xyrion.org/ciff/ + 77. http://www.x3f.info/technotes/FileDocs/X3F_Format.pdf + 78. http://dl.maptools.org/dl/libtiff/tiff-3.8.0.tar.gz + 79. http://www.cybercom.net/~dcoffin/dcraw/libtiff.patch + 80. http://www.cybercom.net/~dcoffin/dcraw/elphel_dng.c + 81. http://www.adobe.com/products/dng/main.html + 82. http://www.adobe.com/products/photoshop/main.html + 83. http://cinepaint.sourceforge.net/ + 84. http://www.cybercom.net/~dcoffin/dcraw/ahd_maze.png + 85. http://www.cybercom.net/~dcoffin/dcraw/vng_grid.png + 86. http://netpbm.sourceforge.net/ + 87. http://www.imagemagick.org/ + 88. http://www.faqs.org/docs/artu/multiprogramchapter.html + 89. http://www.dpreview.com/news/0202/02021101foveonx3.asp + 90. http://e2500.narod.ru/raw_format_e.htm + 91. http://www.inweb.ch/foto/rawformat.html + 92. http://forums.dpreview.com/forums/read.asp?forum=1015&message=4961779 + 93. http://myfototest.narod.ru/ + 94. http://forums.dpreview.com/forums/read.asp?forum=1024&message=11773287 + 95. http://tester13.nm.ru/nikon/ + 96. http://www.camerahacking.com/ + 97. http://dropload.com/ + 98. http://yousendit.com/ + 99. http://www.planet-interkom.de/oliver.hartmann/dc20secr.htm + 100. http://www.itojun.org/diary/19961113/index.eng.html + 101. http://kdc2tiff.sourceforge.net/ diff --git a/README.SuSE b/README.SuSE new file mode 100644 index 0000000..1eb9f6a --- /dev/null +++ b/README.SuSE @@ -0,0 +1,3 @@ +To prevent name clashes, following tools from dcraw were renamed: + +parse -> dcparse diff --git a/badpixels b/badpixels new file mode 100644 index 0000000..c470178 --- /dev/null +++ b/badpixels @@ -0,0 +1,34 @@ +# .badpixels file for my Canon PowerShot G2, serial no. 4624504380 +# dcraw will use this file if run in the same directory, or in any +# subdirectory. + +# Always use "dcraw -d -j -t 0" when locating bad pixels!! + +# Format is: pixel column, pixel row, UNIX time of death + +# This pixel went bad between August 1 and 4, 2002 + 962 91 1028350000 +# This pixel went bad between January 9 and 30, 2003 + 902 877 1043000000 + +# These are only visible in low light, so I haven't dated them: + 621 943 0 +1285 1067 0 +1286 1067 0 +2181 1532 0 + +# I swept these up 4/19/2005 + 763 36 1110000000 +1827 466 1110000000 + +# low light + 198 1288 1110000000 + 346 328 1110000000 + 429 273 1110000000 + 621 943 1110000000 + 624 874 1110000000 + 848 400 1110000000 +1682 1686 1110000000 +1892 1250 1110000000 +2234 484 1110000000 +2242 618 1110000000 diff --git a/dcraw.1 b/dcraw.1 new file mode 100644 index 0000000..07d5092 --- /dev/null +++ b/dcraw.1 @@ -0,0 +1,204 @@ +.\" +.\" Man page for dcraw (Raw Photo Decoder) +.\" +.\" Copyright (c) 2006 by David Coffin +.\" +.\" You may distribute without restriction. +.\" +.\" David Coffin +.\" dcoffin a cybercom o net +.\" http://www.cybercom.net/~dcoffin +.\" +.TH dcraw 1 "July 18, 2006" +.LO 1 +.SH NAME +dcraw - command-line decoder for raw digital photos +.SH SYNOPSIS +.B dcraw +[\fIOPTION\fR]... [\fIFILE\fR]... +.SH DESCRIPTION +.B dcraw +decodes raw photos, displays metadata, and extracts thumbnails. +.SH OPTIONS +.TP +.B -v +Print verbose messages, not just warnings and errors. +.TP +.B -c +Write decoded images or thumbnails to standard output. +.TP +.B -e +Extract the camera-generated thumbnail, not the raw image. +You'll get either a JPEG or a PPM file, depending on the camera. +.TP +.B -z +Change the access and modification times of an AVI, JPEG or raw +file to when the photo was taken, assuming that the camera clock +was set to Universal Time. +.TP +.B -i +Identify files but don't decode them. +Exit status is 0 if +.B dcraw +can decode the last file, 1 if it can't. +.B -i -v +shows metadata. +.TP +.B "" +.B dcraw +cannot decode JPEG files!! +.TP +.B -d +Show the raw data as a grayscale image with no interpolation. +Good for photographing black-and-white documents. +.TP +.B -D +Same as +.BR -d , +but totally raw (no color scaling). +.TP +.B -h +Output a half-size color image. Twice as fast as +.BR -q\ 0 . +.TP +.B -q 0 +Use high-speed, low-quality bilinear interpolation. +.TP +.B -q 2 +Use Variable Number of Gradients (VNG) interpolation. +.TP +.B -q 3 +Use Adaptive Homogeneity-Directed (AHD) interpolation. +.TP +.B -f +Interpolate RGB as four colors. Use this if the output shows +false 2x2 meshes with VNG or mazes with AHD. +.TP +.B -B sigma_domain sigma_range +Use a bilateral filter to smooth noise while preserving edges. +.B sigma_domain +is in units of pixels, while +.B sigma_range +is in units of CIELab colorspace. +Try +.B -B\ 2\ 4 +to start. +.TP +.B -b brightness +By default, +.B dcraw +writes 8-bit PGM/PPM/PAM with a BT.709 gamma curve and a +99th-percentile white point. If the result is too light or +too dark, +.B -b +lets you adjust it. Default is 1.0. +.TP +.B -4 +Write 16-bit linear pseudo-PGM/PPM/PAM with no gamma curve, +no white point, and no +.B -b +option. +.TP +.B -T +Write TIFF output (with metadata) instead of PGM/PPM/PAM. +.TP +.B -k black +Set the black point. Default depends on the camera. +.TP +.B -a +Automatic color balance. The default is to use a fixed +color balance based on a white card photographed in sunlight. +.TP +.B -w +Use the color balance specified by the camera. +If this can't be found, print a warning and revert to the default. +.TP +.B -r mul0 mul1 mul2 mul3 +Specify your own raw color balance. These multipliers can be cut +and pasted from the output of +.BR dcraw\ -v . +.TP +.B -H 0 +Clip all highlights to solid white (default). +.TP +.B -H 1 +Leave highlights unclipped in various shades of pink. +.TP +.B -H 2-9 +Reconstruct highlights. Low numbers favor whites; high numbers +favor colors. Try +.B -H 5 +as a compromise. If that's not good enough, do +.BR -H\ 9 , +cut out the non-white highlights, and paste them into an image +generated with +.BR -H\ 3 . +.TP +.B -m +Same as +.BR -o\ 0 . +.TP +.B -o [0-5] +Select the output colorspace when the +.B -p +option is not used: + +.B \t0 +\ \ Raw color (unique to each camera) +.br +.B \t1 +\ \ sRGB D65 (default) +.br +.B \t2 +\ \ Adobe RGB (1998) D65 +.br +.B \t3 +\ \ Wide Gamut RGB D65 +.br +.B \t4 +\ \ Kodak ProPhoto RGB D65 +.br +.B \t5 +\ \ XYZ +.TP +.BR -p\ camera.icm \ [\ -o\ output.icm \ ] +Use ICC profiles to define the camera's raw colorspace and the +desired output colorspace (sRGB by default). +.TP +.B -p embed +Use the ICC profile embedded in the raw photo. +.TP +.B -t [0-7,90,180,270] +Flip the output image. By default, +.B dcraw +applies the flip specified by the camera. +.B -t 0 +disables all flipping. +.TP +.B -j +For Fuji\ Super\ CCD cameras, show the image tilted 45 degrees, +so that each output pixel corresponds to one raw pixel. +.TP +.B -s +For Fuji\ Super\ CCD\ SR cameras, use the secondary sensors, in +effect underexposing the image by four stops to reveal detail +in the highlights. +.TP +.B "" +For all other cameras, +.B -j +and +.B -s +are silently ignored. +.SH "SEE ALSO" +.BR pgm (5), +.BR ppm (5), +.BR pam (5), +.BR pnmgamma (1), +.BR pnmtotiff (1), +.BR pnmtopng (1), +.BR gphoto2 (1), +.BR cjpeg (1), +.BR djpeg (1) +.SH AUTHOR +Written by David Coffin, dcoffin a cybercom o net diff --git a/dcraw.c b/dcraw.c new file mode 100644 index 0000000..92e9f9f --- /dev/null +++ b/dcraw.c @@ -0,0 +1,7111 @@ +/* + dcraw.c -- Dave Coffin's raw photo decoder + Copyright 1997-2006 by Dave Coffin, dcoffin a cybercom o net + + This is a command-line ANSI C program to convert raw photos from + any digital camera on any computer running any operating system. + + Attention! Some parts of this program are restricted under the + terms of the GNU General Public License. Such code is enclosed + in "BEGIN GPL BLOCK" and "END GPL BLOCK" declarations. + Any code not declared GPL is free for all uses. + + Starting in Revision 1.237, the code to support Foveon cameras + is under GPL. + + To lawfully redistribute dcraw.c, you must either (a) include + full source code for all executable files containing restricted + functions, (b) remove these functions, re-implement them, or + copy them from an earlier, non-GPL Revision of dcraw.c, or (c) + purchase a license from the author. + + $Revision: 1.353 $ + $Date: 2006/10/10 04:46:02 $ + */ + +#define VERSION "8.40" + +#define _GNU_SOURCE +#define _USE_MATH_DEFINES +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* + NO_JPEG disables decoding of compressed Kodak DC120 files. + NO_LCMS disables the "-p" option. + */ +#ifndef NO_JPEG +#include +#endif +#ifndef NO_LCMS +#include +#endif + +#ifdef __CYGWIN__ +#include +#endif +#ifdef WIN32 +#include +#include +#pragma comment(lib, "ws2_32.lib") +#define strcasecmp stricmp +typedef __int64 INT64; +typedef unsigned __int64 UINT64; +#else +#include +#include +#include +typedef long long INT64; +typedef unsigned long long UINT64; +#endif + +#ifdef LJPEG_DECODE +#error Please compile dcraw.c by itself. +#error Do not link it with ljpeg_decode. +#endif + +#ifndef LONG_BIT +#define LONG_BIT (8 * sizeof (long)) +#endif + +#define ushort UshORt +typedef unsigned char uchar; +typedef unsigned short ushort; + +/* + All global variables are defined here, and all functions that + access them are prefixed with "CLASS". Note that a thread-safe + C++ class cannot have non-const static local variables. + */ +FILE *ifp; +short order; +char *ifname, make[64], model[72], model2[64], *meta_data, cdesc[5]; +float flash_used, canon_ev, iso_speed, shutter, aperture, focal_len; +time_t timestamp; +unsigned shot_order, kodak_cbpp, filters, unique_id, *oprof; +int profile_offset, profile_length; +int thumb_offset, thumb_length, thumb_width, thumb_height, thumb_misc; +int data_offset, strip_offset, curve_offset, meta_offset, meta_length; +int tiff_nifds, tiff_flip, tiff_bps, tiff_compress, tile_length; +int raw_height, raw_width, top_margin, left_margin; +int height, width, fuji_width, colors, tiff_samples; +int black, maximum, raw_color, use_gamma; +int iheight, iwidth, shrink, flip, xmag, ymag; +int zero_after_ff, is_raw, dng_version, is_foveon; +ushort (*image)[4], white[8][8], curve[0x1000], cr2_slice[3]; +float bright=1, user_mul[4]={0,0,0,0}, sigma_d=0, sigma_r=0; +int four_color_rgb=0, document_mode=0, highlight=0; +int verbose=0, use_auto_wb=0, use_camera_wb=0; +int output_color=1, output_bps=8, output_tiff=0; +int fuji_layout, fuji_secondary, use_secondary=0; +float cam_mul[4], pre_mul[4], rgb_cam[3][4]; /* RGB from camera color */ +const double xyz_rgb[3][3] = { /* XYZ from RGB */ + { 0.412453, 0.357580, 0.180423 }, + { 0.212671, 0.715160, 0.072169 }, + { 0.019334, 0.119193, 0.950227 } }; +const float d65_white[3] = { 0.950456, 1, 1.088754 }; +int histogram[4][0x2000]; +void (*write_thumb)(FILE *), (*write_fun)(FILE *); +void (*load_raw)(), (*thumb_load_raw)(); +jmp_buf failure; + +struct decode { + struct decode *branch[2]; + int leaf; +} first_decode[2048], *second_decode, *free_decode; + +struct { + int width, height, bps, comp, phint, offset, flip, samples, bytes; +} tiff_ifd[10]; + +struct { + int format, key_off, black, black_off, split_col, tag_21a; + float tag_210; +} ph1; + +#define CLASS + +#define FORC3 for (c=0; c < 3; c++) +#define FORC4 for (c=0; c < 4; c++) +#define FORCC for (c=0; c < colors; c++) + +#define SQR(x) ((x)*(x)) +#define ABS(x) (((int)(x) ^ ((int)(x) >> 31)) - ((int)(x) >> 31)) +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#define LIM(x,min,max) MAX(min,MIN(x,max)) +#define ULIM(x,y,z) ((y) < (z) ? LIM(x,y,z) : LIM(x,z,y)) +#define CLIP(x) LIM(x,0,65535) +#define SWAP(a,b) { a ^= b; a ^= (b ^= a); } + +/* + In order to inline this calculation, I make the risky + assumption that all filter patterns can be described + by a repeating pattern of eight rows and two columns + + Do not use the FC or BAYER macros with the Leaf CatchLight, + because its pattern is 16x16, not 2x8. + + Return values are either 0/1/2/3 = G/M/C/Y or 0/1/2/3 = R/G1/B/G2 + + PowerShot 600 PowerShot A50 PowerShot Pro70 Pro90 & G1 + 0xe1e4e1e4: 0x1b4e4b1e: 0x1e4b4e1b: 0xb4b4b4b4: + + 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 + 0 G M G M G M 0 C Y C Y C Y 0 Y C Y C Y C 0 G M G M G M + 1 C Y C Y C Y 1 M G M G M G 1 M G M G M G 1 Y C Y C Y C + 2 M G M G M G 2 Y C Y C Y C 2 C Y C Y C Y + 3 C Y C Y C Y 3 G M G M G M 3 G M G M G M + 4 C Y C Y C Y 4 Y C Y C Y C + PowerShot A5 5 G M G M G M 5 G M G M G M + 0x1e4e1e4e: 6 Y C Y C Y C 6 C Y C Y C Y + 7 M G M G M G 7 M G M G M G + 0 1 2 3 4 5 + 0 C Y C Y C Y + 1 G M G M G M + 2 C Y C Y C Y + 3 M G M G M G + + All RGB cameras use one of these Bayer grids: + + 0x16161616: 0x61616161: 0x49494949: 0x94949494: + + 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 0 1 2 3 4 5 + 0 B G B G B G 0 G R G R G R 0 G B G B G B 0 R G R G R G + 1 G R G R G R 1 B G B G B G 1 R G R G R G 1 G B G B G B + 2 B G B G B G 2 G R G R G R 2 G B G B G B 2 R G R G R G + 3 G R G R G R 3 B G B G B G 3 R G R G R G 3 G B G B G B + */ + +#define FC(row,col) \ + (filters >> ((((row) << 1 & 14) + ((col) & 1)) << 1) & 3) + +#define BAYER(row,col) \ + image[((row) >> shrink)*iwidth + ((col) >> shrink)][FC(row,col)] + +#define BAYER2(row,col) \ + image[((row) >> shrink)*iwidth + ((col) >> shrink)][fc(row,col)] + +int CLASS fc (int row, int col) +{ + static const char filter[16][16] = + { { 2,1,1,3,2,3,2,0,3,2,3,0,1,2,1,0 }, + { 0,3,0,2,0,1,3,1,0,1,1,2,0,3,3,2 }, + { 2,3,3,2,3,1,1,3,3,1,2,1,2,0,0,3 }, + { 0,1,0,1,0,2,0,2,2,0,3,0,1,3,2,1 }, + { 3,1,1,2,0,1,0,2,1,3,1,3,0,1,3,0 }, + { 2,0,0,3,3,2,3,1,2,0,2,0,3,2,2,1 }, + { 2,3,3,1,2,1,2,1,2,1,1,2,3,0,0,1 }, + { 1,0,0,2,3,0,0,3,0,3,0,3,2,1,2,3 }, + { 2,3,3,1,1,2,1,0,3,2,3,0,2,3,1,3 }, + { 1,0,2,0,3,0,3,2,0,1,1,2,0,1,0,2 }, + { 0,1,1,3,3,2,2,1,1,3,3,0,2,1,3,2 }, + { 2,3,2,0,0,1,3,0,2,0,1,2,3,0,1,0 }, + { 1,3,1,2,3,2,3,2,0,2,0,1,1,0,3,0 }, + { 0,2,0,3,1,0,0,1,1,3,3,2,3,2,2,1 }, + { 2,1,3,2,3,1,2,1,0,3,0,2,0,2,0,2 }, + { 0,3,1,0,0,2,0,3,2,1,3,1,1,3,1,3 } }; + + if (filters != 1) return FC(row,col); + return filter[(row+top_margin) & 15][(col+left_margin) & 15]; +} + +#ifndef __GLIBC__ +char *my_memmem (char *haystack, size_t haystacklen, + char *needle, size_t needlelen) +{ + char *c; + for (c = haystack; c <= haystack + haystacklen - needlelen; c++) + if (!memcmp (c, needle, needlelen)) + return c; + return NULL; +} +#define memmem my_memmem +#endif + +void CLASS merror (void *ptr, char *where) +{ + if (ptr) return; + fprintf (stderr, "%s: Out of memory in %s\n", ifname, where); + longjmp (failure, 1); +} + +ushort CLASS sget2 (uchar *s) +{ + if (order == 0x4949) /* "II" means little-endian */ + return s[0] | s[1] << 8; + else /* "MM" means big-endian */ + return s[0] << 8 | s[1]; +} + +ushort CLASS get2() +{ + uchar str[2] = { 0xff,0xff }; + fread (str, 1, 2, ifp); + return sget2(str); +} + +int CLASS sget4 (uchar *s) +{ + if (order == 0x4949) + return s[0] | s[1] << 8 | s[2] << 16 | s[3] << 24; + else + return s[0] << 24 | s[1] << 16 | s[2] << 8 | s[3]; +} +#define sget4(s) sget4((uchar *)s) + +int CLASS get4() +{ + uchar str[4] = { 0xff,0xff,0xff,0xff }; + fread (str, 1, 4, ifp); + return sget4(str); +} + +int CLASS getint (int type) +{ + return type == 3 ? get2() : get4(); +} + +float CLASS int_to_float (int i) +{ + union { int i; float f; } u; + u.i = i; + return u.f; +} + +double CLASS getreal (int type) +{ + union { char c[8]; double d; } u; + int i, rev; + + switch (type) { + case 3: return (unsigned short) get2(); + case 4: return (unsigned int) get4(); + case 5: u.d = (unsigned int) get4(); + return u.d / (unsigned int) get4(); + case 8: return (signed short) get2(); + case 9: return (signed int) get4(); + case 10: u.d = (signed int) get4(); + return u.d / (signed int) get4(); + case 11: return int_to_float (get4()); + case 12: + rev = 7 * ((order == 0x4949) == (ntohs(0x1234) == 0x1234)); + for (i=0; i < 8; i++) + u.c[i ^ rev] = fgetc(ifp); + return u.d; + default: return fgetc(ifp); + } +} +#define getrat() getreal(10) + +void CLASS read_shorts (ushort *pixel, int count) +{ + fread (pixel, 2, count, ifp); + if ((order == 0x4949) == (ntohs(0x1234) == 0x1234)) + swab (pixel, pixel, count*2); +} + +void CLASS canon_600_fixed_wb (int temp) +{ + static const short mul[4][5] = { + { 667, 358,397,565,452 }, + { 731, 390,367,499,517 }, + { 1119, 396,348,448,537 }, + { 1399, 485,431,508,688 } }; + int lo, hi, i; + float frac=0; + + for (lo=4; --lo; ) + if (*mul[lo] <= temp) break; + for (hi=0; hi < 3; hi++) + if (*mul[hi] >= temp) break; + if (lo != hi) + frac = (float) (temp - *mul[lo]) / (*mul[hi] - *mul[lo]); + for (i=1; i < 5; i++) + pre_mul[i-1] = 1 / (frac * mul[hi][i] + (1-frac) * mul[lo][i]); +} + +/* Return values: 0 = white 1 = near white 2 = not white */ +int CLASS canon_600_color (int ratio[2], int mar) +{ + int clipped=0, target, miss; + + if (flash_used) { + if (ratio[1] < -104) + { ratio[1] = -104; clipped = 1; } + if (ratio[1] > 12) + { ratio[1] = 12; clipped = 1; } + } else { + if (ratio[1] < -264 || ratio[1] > 461) return 2; + if (ratio[1] < -50) + { ratio[1] = -50; clipped = 1; } + if (ratio[1] > 307) + { ratio[1] = 307; clipped = 1; } + } + target = flash_used || ratio[1] < 197 + ? -38 - (398 * ratio[1] >> 10) + : -123 + (48 * ratio[1] >> 10); + if (target - mar <= ratio[0] && + target + 20 >= ratio[0] && !clipped) return 0; + miss = target - ratio[0]; + if (abs(miss) >= mar*4) return 2; + if (miss < -20) miss = -20; + if (miss > mar) miss = mar; + ratio[0] = target - miss; + return 1; +} + +void CLASS canon_600_auto_wb() +{ + int mar, row, col, i, j, st, count[] = { 0,0 }; + int test[8], total[2][8], ratio[2][2], stat[2]; + + memset (&total, 0, sizeof total); + i = canon_ev + 0.5; + if (i < 10) mar = 150; + else if (i > 12) mar = 20; + else mar = 280 - 20 * i; + if (flash_used) mar = 80; + for (row=14; row < height-14; row+=4) + for (col=10; col < width; col+=2) { + for (i=0; i < 8; i++) + test[(i & 4) + FC(row+(i >> 1),col+(i & 1))] = + BAYER(row+(i >> 1),col+(i & 1)); + for (i=0; i < 8; i++) + if (test[i] < 150 || test[i] > 1500) goto next; + for (i=0; i < 4; i++) + if (abs(test[i] - test[i+4]) > 50) goto next; + for (i=0; i < 2; i++) { + for (j=0; j < 4; j+=2) + ratio[i][j >> 1] = ((test[i*4+j+1]-test[i*4+j]) << 10) / test[i*4+j]; + stat[i] = canon_600_color (ratio[i], mar); + } + if ((st = stat[0] | stat[1]) > 1) goto next; + for (i=0; i < 2; i++) + if (stat[i]) + for (j=0; j < 2; j++) + test[i*4+j*2+1] = test[i*4+j*2] * (0x400 + ratio[i][j]) >> 10; + for (i=0; i < 8; i++) + total[st][i] += test[i]; + count[st]++; +next: continue; + } + if (count[0] | count[1]) { + st = count[0]*200 < count[1]; + for (i=0; i < 4; i++) + pre_mul[i] = 1.0 / (total[st][i] + total[st][i+4]); + } +} + +void CLASS canon_600_coeff() +{ + static const short table[6][12] = { + { -190,702,-1878,2390, 1861,-1349,905,-393, -432,944,2617,-2105 }, + { -1203,1715,-1136,1648, 1388,-876,267,245, -1641,2153,3921,-3409 }, + { -615,1127,-1563,2075, 1437,-925,509,3, -756,1268,2519,-2007 }, + { -190,702,-1886,2398, 2153,-1641,763,-251, -452,964,3040,-2528 }, + { -190,702,-1878,2390, 1861,-1349,905,-393, -432,944,2617,-2105 }, + { -807,1319,-1785,2297, 1388,-876,769,-257, -230,742,2067,-1555 } }; + int t=0, i, c; + float mc, yc; + + mc = pre_mul[1] / pre_mul[2]; + yc = pre_mul[3] / pre_mul[2]; + if (mc > 1 && mc <= 1.28 && yc < 0.8789) t=1; + if (mc > 1.28 && mc <= 2) { + if (yc < 0.8789) t=3; + else if (yc <= 2) t=4; + } + if (flash_used) t=5; + for (raw_color = i=0; i < 3; i++) + FORCC rgb_cam[i][c] = table[t][i*4 + c] / 1024.0; +} + +void CLASS canon_600_load_raw() +{ + uchar data[1120], *dp; + ushort pixel[896], *pix; + int irow, row, col, val; + static const short mul[4][2] = + { { 1141,1145 }, { 1128,1109 }, { 1178,1149 }, { 1128,1109 } }; + + for (irow=row=0; irow < height; irow++) + { + fread (data, raw_width * 10 / 8, 1, ifp); + for (dp=data, pix=pixel; dp < data+1120; dp+=10, pix+=8) + { + pix[0] = (dp[0] << 2) + (dp[1] >> 6 ); + pix[1] = (dp[2] << 2) + (dp[1] >> 4 & 3); + pix[2] = (dp[3] << 2) + (dp[1] >> 2 & 3); + pix[3] = (dp[4] << 2) + (dp[1] & 3); + pix[4] = (dp[5] << 2) + (dp[9] & 3); + pix[5] = (dp[6] << 2) + (dp[9] >> 2 & 3); + pix[6] = (dp[7] << 2) + (dp[9] >> 4 & 3); + pix[7] = (dp[8] << 2) + (dp[9] >> 6 ); + } + for (col=0; col < width; col++) + BAYER(row,col) = pixel[col]; + for (col=width; col < raw_width; col++) + black += pixel[col]; + if ((row+=2) > height) row = 1; + } + if (raw_width > width) + black = black / ((raw_width - width) * height) - 4; + for (row=0; row < height; row++) + for (col=0; col < width; col++) { + val = (BAYER(row,col) - black) * mul[row & 3][col & 1] >> 9; + if (val < 0) val = 0; + BAYER(row,col) = val; + } + canon_600_fixed_wb(1311); + canon_600_auto_wb(); + canon_600_coeff(); + maximum = (0x3ff - black) * 1109 >> 9; + black = 0; +} + +void CLASS canon_a5_load_raw() +{ + uchar data[1940], *dp; + ushort pixel[1552], *pix; + int row, col; + + for (row=0; row < height; row++) { + fread (data, raw_width * 10 / 8, 1, ifp); + for (dp=data, pix=pixel; pix < pixel+raw_width; dp+=10, pix+=8) + { + pix[0] = (dp[1] << 2) + (dp[0] >> 6); + pix[1] = (dp[0] << 4) + (dp[3] >> 4); + pix[2] = (dp[3] << 6) + (dp[2] >> 2); + pix[3] = (dp[2] << 8) + (dp[5] ); + pix[4] = (dp[4] << 2) + (dp[7] >> 6); + pix[5] = (dp[7] << 4) + (dp[6] >> 4); + pix[6] = (dp[6] << 6) + (dp[9] >> 2); + pix[7] = (dp[9] << 8) + (dp[8] ); + } + for (col=0; col < width; col++) + BAYER(row,col) = (pixel[col] & 0x3ff); + for (col=width; col < raw_width; col++) + black += pixel[col] & 0x3ff; + } + if (raw_width > width) + black /= (raw_width - width) * height; + maximum = 0x3ff; +} + +/* + getbits(-1) initializes the buffer + getbits(n) where 0 <= n <= 25 returns an n-bit integer + */ +unsigned CLASS getbits (int nbits) +{ + static unsigned bitbuf=0; + static int vbits=0, reset=0; + unsigned c; + + if (nbits == -1) + return bitbuf = vbits = reset = 0; + if (nbits == 0 || reset) return 0; + while (vbits < nbits) { + c = fgetc(ifp); + if ((reset = zero_after_ff && c == 0xff && fgetc(ifp))) return 0; + bitbuf = (bitbuf << 8) + c; + vbits += 8; + } + vbits -= nbits; + return bitbuf << (32-nbits-vbits) >> (32-nbits); +} + +void CLASS init_decoder() +{ + memset (first_decode, 0, sizeof first_decode); + free_decode = first_decode; +} + +/* + Construct a decode tree according the specification in *source. + The first 16 bytes specify how many codes should be 1-bit, 2-bit + 3-bit, etc. Bytes after that are the leaf values. + + For example, if the source is + + { 0,1,4,2,3,1,2,0,0,0,0,0,0,0,0,0, + 0x04,0x03,0x05,0x06,0x02,0x07,0x01,0x08,0x09,0x00,0x0a,0x0b,0xff }, + + then the code is + + 00 0x04 + 010 0x03 + 011 0x05 + 100 0x06 + 101 0x02 + 1100 0x07 + 1101 0x01 + 11100 0x08 + 11101 0x09 + 11110 0x00 + 111110 0x0a + 1111110 0x0b + 1111111 0xff + */ +uchar * CLASS make_decoder (const uchar *source, int level) +{ + struct decode *cur; + static int leaf; + int i, next; + + if (level==0) leaf=0; + cur = free_decode++; + if (free_decode > first_decode+2048) { + fprintf (stderr, "%s: decoder table overflow\n", ifname); + longjmp (failure, 2); + } + for (i=next=0; i <= leaf && next < 16; ) + i += source[next++]; + if (i > leaf) { + if (level < next) { + cur->branch[0] = free_decode; + make_decoder (source, level+1); + cur->branch[1] = free_decode; + make_decoder (source, level+1); + } else + cur->leaf = source[16 + leaf++]; + } + return (uchar *) source + 16 + leaf; +} + +void CLASS crw_init_tables (unsigned table) +{ + static const uchar first_tree[3][29] = { + { 0,1,4,2,3,1,2,0,0,0,0,0,0,0,0,0, + 0x04,0x03,0x05,0x06,0x02,0x07,0x01,0x08,0x09,0x00,0x0a,0x0b,0xff }, + { 0,2,2,3,1,1,1,1,2,0,0,0,0,0,0,0, + 0x03,0x02,0x04,0x01,0x05,0x00,0x06,0x07,0x09,0x08,0x0a,0x0b,0xff }, + { 0,0,6,3,1,1,2,0,0,0,0,0,0,0,0,0, + 0x06,0x05,0x07,0x04,0x08,0x03,0x09,0x02,0x00,0x0a,0x01,0x0b,0xff }, + }; + static const uchar second_tree[3][180] = { + { 0,2,2,2,1,4,2,1,2,5,1,1,0,0,0,139, + 0x03,0x04,0x02,0x05,0x01,0x06,0x07,0x08, + 0x12,0x13,0x11,0x14,0x09,0x15,0x22,0x00,0x21,0x16,0x0a,0xf0, + 0x23,0x17,0x24,0x31,0x32,0x18,0x19,0x33,0x25,0x41,0x34,0x42, + 0x35,0x51,0x36,0x37,0x38,0x29,0x79,0x26,0x1a,0x39,0x56,0x57, + 0x28,0x27,0x52,0x55,0x58,0x43,0x76,0x59,0x77,0x54,0x61,0xf9, + 0x71,0x78,0x75,0x96,0x97,0x49,0xb7,0x53,0xd7,0x74,0xb6,0x98, + 0x47,0x48,0x95,0x69,0x99,0x91,0xfa,0xb8,0x68,0xb5,0xb9,0xd6, + 0xf7,0xd8,0x67,0x46,0x45,0x94,0x89,0xf8,0x81,0xd5,0xf6,0xb4, + 0x88,0xb1,0x2a,0x44,0x72,0xd9,0x87,0x66,0xd4,0xf5,0x3a,0xa7, + 0x73,0xa9,0xa8,0x86,0x62,0xc7,0x65,0xc8,0xc9,0xa1,0xf4,0xd1, + 0xe9,0x5a,0x92,0x85,0xa6,0xe7,0x93,0xe8,0xc1,0xc6,0x7a,0x64, + 0xe1,0x4a,0x6a,0xe6,0xb3,0xf1,0xd3,0xa5,0x8a,0xb2,0x9a,0xba, + 0x84,0xa4,0x63,0xe5,0xc5,0xf3,0xd2,0xc4,0x82,0xaa,0xda,0xe4, + 0xf2,0xca,0x83,0xa3,0xa2,0xc3,0xea,0xc2,0xe2,0xe3,0xff,0xff }, + { 0,2,2,1,4,1,4,1,3,3,1,0,0,0,0,140, + 0x02,0x03,0x01,0x04,0x05,0x12,0x11,0x06, + 0x13,0x07,0x08,0x14,0x22,0x09,0x21,0x00,0x23,0x15,0x31,0x32, + 0x0a,0x16,0xf0,0x24,0x33,0x41,0x42,0x19,0x17,0x25,0x18,0x51, + 0x34,0x43,0x52,0x29,0x35,0x61,0x39,0x71,0x62,0x36,0x53,0x26, + 0x38,0x1a,0x37,0x81,0x27,0x91,0x79,0x55,0x45,0x28,0x72,0x59, + 0xa1,0xb1,0x44,0x69,0x54,0x58,0xd1,0xfa,0x57,0xe1,0xf1,0xb9, + 0x49,0x47,0x63,0x6a,0xf9,0x56,0x46,0xa8,0x2a,0x4a,0x78,0x99, + 0x3a,0x75,0x74,0x86,0x65,0xc1,0x76,0xb6,0x96,0xd6,0x89,0x85, + 0xc9,0xf5,0x95,0xb4,0xc7,0xf7,0x8a,0x97,0xb8,0x73,0xb7,0xd8, + 0xd9,0x87,0xa7,0x7a,0x48,0x82,0x84,0xea,0xf4,0xa6,0xc5,0x5a, + 0x94,0xa4,0xc6,0x92,0xc3,0x68,0xb5,0xc8,0xe4,0xe5,0xe6,0xe9, + 0xa2,0xa3,0xe3,0xc2,0x66,0x67,0x93,0xaa,0xd4,0xd5,0xe7,0xf8, + 0x88,0x9a,0xd7,0x77,0xc4,0x64,0xe2,0x98,0xa5,0xca,0xda,0xe8, + 0xf3,0xf6,0xa9,0xb2,0xb3,0xf2,0xd2,0x83,0xba,0xd3,0xff,0xff }, + { 0,0,6,2,1,3,3,2,5,1,2,2,8,10,0,117, + 0x04,0x05,0x03,0x06,0x02,0x07,0x01,0x08, + 0x09,0x12,0x13,0x14,0x11,0x15,0x0a,0x16,0x17,0xf0,0x00,0x22, + 0x21,0x18,0x23,0x19,0x24,0x32,0x31,0x25,0x33,0x38,0x37,0x34, + 0x35,0x36,0x39,0x79,0x57,0x58,0x59,0x28,0x56,0x78,0x27,0x41, + 0x29,0x77,0x26,0x42,0x76,0x99,0x1a,0x55,0x98,0x97,0xf9,0x48, + 0x54,0x96,0x89,0x47,0xb7,0x49,0xfa,0x75,0x68,0xb6,0x67,0x69, + 0xb9,0xb8,0xd8,0x52,0xd7,0x88,0xb5,0x74,0x51,0x46,0xd9,0xf8, + 0x3a,0xd6,0x87,0x45,0x7a,0x95,0xd5,0xf6,0x86,0xb4,0xa9,0x94, + 0x53,0x2a,0xa8,0x43,0xf5,0xf7,0xd4,0x66,0xa7,0x5a,0x44,0x8a, + 0xc9,0xe8,0xc8,0xe7,0x9a,0x6a,0x73,0x4a,0x61,0xc7,0xf4,0xc6, + 0x65,0xe9,0x72,0xe6,0x71,0x91,0x93,0xa6,0xda,0x92,0x85,0x62, + 0xf3,0xc5,0xb2,0xa4,0x84,0xba,0x64,0xa5,0xb3,0xd2,0x81,0xe5, + 0xd3,0xaa,0xc4,0xca,0xf2,0xb1,0xe4,0xd1,0x83,0x63,0xea,0xc3, + 0xe2,0x82,0xf1,0xa3,0xc2,0xa1,0xc1,0xe3,0xa2,0xe1,0xff,0xff } + }; + if (table > 2) table = 2; + init_decoder(); + make_decoder ( first_tree[table], 0); + second_decode = free_decode; + make_decoder (second_tree[table], 0); +} + +/* + Return 0 if the image starts with compressed data, + 1 if it starts with uncompressed low-order bits. + + In Canon compressed data, 0xff is always followed by 0x00. + */ +int CLASS canon_has_lowbits() +{ + uchar test[0x4000]; + int ret=1, i; + + fseek (ifp, 0, SEEK_SET); + fread (test, 1, sizeof test, ifp); + for (i=540; i < sizeof test - 1; i++) + if (test[i] == 0xff) { + if (test[i+1]) return 1; + ret=0; + } + return ret; +} + +void CLASS canon_compressed_load_raw() +{ + ushort *pixel, *prow; + int lowbits, i, row, r, col, save, val; + unsigned irow, icol; + struct decode *decode, *dindex; + int block, diffbuf[64], leaf, len, diff, carry=0, pnum=0, base[2]; + uchar c; + + crw_init_tables (tiff_compress); + pixel = (ushort *) calloc (raw_width*8, sizeof *pixel); + merror (pixel, "canon_compressed_load_raw()"); + lowbits = canon_has_lowbits(); + if (!lowbits) maximum = 0x3ff; + fseek (ifp, 540 + lowbits*raw_height*raw_width/4, SEEK_SET); + zero_after_ff = 1; + getbits(-1); + for (row=0; row < raw_height; row+=8) { + for (block=0; block < raw_width >> 3; block++) { + memset (diffbuf, 0, sizeof diffbuf); + decode = first_decode; + for (i=0; i < 64; i++ ) { + for (dindex=decode; dindex->branch[0]; ) + dindex = dindex->branch[getbits(1)]; + leaf = dindex->leaf; + decode = second_decode; + if (leaf == 0 && i) break; + if (leaf == 0xff) continue; + i += leaf >> 4; + len = leaf & 15; + if (len == 0) continue; + diff = getbits(len); + if ((diff & (1 << (len-1))) == 0) + diff -= (1 << len) - 1; + if (i < 64) diffbuf[i] = diff; + } + diffbuf[0] += carry; + carry = diffbuf[0]; + for (i=0; i < 64; i++ ) { + if (pnum++ % raw_width == 0) + base[0] = base[1] = 512; + pixel[(block << 6) + i] = ( base[i & 1] += diffbuf[i] ); + } + } + if (lowbits) { + save = ftell(ifp); + fseek (ifp, 26 + row*raw_width/4, SEEK_SET); + for (prow=pixel, i=0; i < raw_width*2; i++) { + c = fgetc(ifp); + for (r=0; r < 8; r+=2, prow++) { + val = (*prow << 2) + ((c >> r) & 3); + if (raw_width == 2672 && val < 512) val += 2; + *prow = val; + } + } + fseek (ifp, save, SEEK_SET); + } + for (r=0; r < 8; r++) { + irow = row - top_margin + r; + if (irow >= height) continue; + for (col=0; col < raw_width; col++) { + icol = col - left_margin; + if (icol < width) + BAYER(irow,icol) = pixel[r*raw_width+col]; + else + black += pixel[r*raw_width+col]; + } + } + } + free (pixel); + if (raw_width > width) + black /= (raw_width - width) * height; +} + +/* + Not a full implementation of Lossless JPEG, just + enough to decode Canon, Kodak and Adobe DNG images. + */ +struct jhead { + int bits, high, wide, clrs, restart, vpred[4]; + struct decode *huff[4]; + ushort *row; +}; + +int CLASS ljpeg_start (struct jhead *jh, int info_only) +{ + int i, tag, len; + uchar data[0x10000], *dp; + + init_decoder(); + for (i=0; i < 4; i++) + jh->huff[i] = free_decode; + jh->restart = INT_MAX; + fread (data, 2, 1, ifp); + if (data[1] != 0xd8) return 0; + do { + fread (data, 2, 2, ifp); + tag = data[0] << 8 | data[1]; + len = (data[2] << 8 | data[3]) - 2; + if (tag <= 0xff00) return 0; + fread (data, 1, len, ifp); + switch (tag) { + case 0xffc0: + case 0xffc3: + jh->bits = data[0]; + jh->high = data[1] << 8 | data[2]; + jh->wide = data[3] << 8 | data[4]; + jh->clrs = data[5]; + break; + case 0xffc4: + if (info_only) break; + for (dp = data; dp < data+len && *dp < 4; ) { + jh->huff[*dp] = free_decode; + dp = make_decoder (++dp, 0); + } + break; + case 0xffdd: + jh->restart = data[0] << 8 | data[1]; + } + } while (tag != 0xffda); + if (info_only) return 1; + jh->row = (ushort *) calloc (jh->wide*jh->clrs, 2); + merror (jh->row, " jpeg_start()"); + return zero_after_ff = 1; +} + +int CLASS ljpeg_diff (struct decode *dindex) +{ + int len, diff; + + while (dindex->branch[0]) + dindex = dindex->branch[getbits(1)]; + len = dindex->leaf; + if (len == 16 && (!dng_version || dng_version >= 0x1010000)) + return -32768; + diff = getbits(len); + if ((diff & (1 << (len-1))) == 0) + diff -= (1 << len) - 1; + return diff; +} + +void CLASS ljpeg_row (int jrow, struct jhead *jh) +{ + int col, c, diff; + ushort mark=0, *outp=jh->row; + + if (jrow * jh->wide % jh->restart == 0) { + FORC4 jh->vpred[c] = 1 << (jh->bits-1); + if (jrow) + do mark = (mark << 8) + (c = fgetc(ifp)); + while (c != EOF && mark >> 4 != 0xffd); + getbits(-1); + } + for (col=0; col < jh->wide; col++) + for (c=0; c < jh->clrs; c++) { + diff = ljpeg_diff (jh->huff[c]); + *outp = col ? outp[-jh->clrs]+diff : (jh->vpred[c] += diff); + outp++; + } +} + +void CLASS lossless_jpeg_load_raw() +{ + int jwide, jrow, jcol, val, jidx, i, j, row=0, col=0; + struct jhead jh; + int min=INT_MAX; + + if (!ljpeg_start (&jh, 0)) return; + jwide = jh.wide * jh.clrs; + + for (jrow=0; jrow < jh.high; jrow++) { + ljpeg_row (jrow, &jh); + for (jcol=0; jcol < jwide; jcol++) { + val = jh.row[jcol]; + if (jh.bits <= 12) + val = curve[val]; + if (cr2_slice[0]) { + jidx = jrow*jwide + jcol; + i = jidx / (cr2_slice[1]*jh.high); + if ((j = i >= cr2_slice[0])) + i = cr2_slice[0]; + jidx -= i * (cr2_slice[1]*jh.high); + row = jidx / cr2_slice[1+j]; + col = jidx % cr2_slice[1+j] + i*cr2_slice[1]; + } + if ((unsigned) (row-top_margin) < height) { + if ((unsigned) (col-left_margin) < width) { + BAYER(row-top_margin,col-left_margin) = val; + if (min > val) min = val; + } else black += val; + } + if (++col >= raw_width) + col = (row++,0); + } + } + free (jh.row); + if (raw_width > width) + black /= (raw_width - width) * height; + if (!strcasecmp(make,"KODAK")) + black = min; +} + +void CLASS adobe_copy_pixel (int row, int col, ushort **rp) +{ + unsigned r, c; + + r = row -= top_margin; + c = col -= left_margin; + if (fuji_secondary && use_secondary) (*rp)++; + if (filters) { + if (fuji_width) { + r = row + fuji_width - 1 - (col >> 1); + c = row + ((col+1) >> 1); + } + if (r < height && c < width) + BAYER(r,c) = **rp < 0x1000 ? curve[**rp] : **rp; + *rp += 1 + fuji_secondary; + } else { + if (r < height && c < width) + for (c=0; c < tiff_samples; c++) + image[row*width+col][c] = (*rp)[c] < 0x1000 ? curve[(*rp)[c]]:(*rp)[c]; + *rp += tiff_samples; + } + if (fuji_secondary && use_secondary) (*rp)--; +} + +void CLASS adobe_dng_load_raw_lj() +{ + int save, twide, trow=0, tcol=0, jrow, jcol; + struct jhead jh; + ushort *rp; + + while (1) { + save = ftell(ifp); + fseek (ifp, get4(), SEEK_SET); + if (!ljpeg_start (&jh, 0)) break; + if (trow >= raw_height) break; + if (jh.high > raw_height-trow) + jh.high = raw_height-trow; + twide = jh.wide; + if (filters) twide *= jh.clrs; + else colors = jh.clrs; + if (fuji_secondary) twide /= 2; + if (twide > raw_width-tcol) + twide = raw_width-tcol; + + for (jrow=0; jrow < jh.high; jrow++) { + ljpeg_row (jrow, &jh); + for (rp=jh.row, jcol=0; jcol < twide; jcol++) + adobe_copy_pixel (trow+jrow, tcol+jcol, &rp); + } + fseek (ifp, save+4, SEEK_SET); + if ((tcol += twide) >= raw_width) { + tcol = 0; + trow += jh.high; + } + free (jh.row); + } +} + +void CLASS adobe_dng_load_raw_nc() +{ + ushort *pixel, *rp; + int row, col; + + pixel = (ushort *) calloc (raw_width * tiff_samples, sizeof *pixel); + merror (pixel, "adobe_dng_load_raw_nc()"); + for (row=0; row < raw_height; row++) { + if (tiff_bps == 16) + read_shorts (pixel, raw_width * tiff_samples); + else { + getbits(-1); + for (col=0; col < raw_width * tiff_samples; col++) + pixel[col] = getbits(tiff_bps); + } + for (rp=pixel, col=0; col < raw_width; col++) + adobe_copy_pixel (row, col, &rp); + } + free (pixel); +} + +void CLASS nikon_compressed_load_raw() +{ + static const uchar nikon_tree[] = { + 0,1,5,1,1,1,1,1,1,2,0,0,0,0,0,0, + 5,4,3,6,2,7,1,0,8,9,11,10,12 + }; + int csize, row, col, i, diff; + ushort vpred[4], hpred[2], *curve; + + init_decoder(); + make_decoder (nikon_tree, 0); + + fseek (ifp, curve_offset, SEEK_SET); + read_shorts (vpred, 4); + csize = get2(); + curve = (ushort *) calloc (csize, sizeof *curve); + merror (curve, "nikon_compressed_load_raw()"); + read_shorts (curve, csize); + + fseek (ifp, data_offset, SEEK_SET); + getbits(-1); + + for (row=0; row < height; row++) + for (col=0; col < raw_width; col++) + { + diff = ljpeg_diff (first_decode); + if (col < 2) { + i = 2*(row & 1) + (col & 1); + vpred[i] += diff; + hpred[col] = vpred[i]; + } else + hpred[col & 1] += diff; + if ((unsigned) (col-left_margin) >= width) continue; + diff = hpred[col & 1]; + if (diff >= csize) diff = csize-1; + BAYER(row,col-left_margin) = curve[diff]; + } + free (curve); +} + +void CLASS nikon_load_raw() +{ + int irow, row, col, i; + + getbits(-1); + for (irow=0; irow < height; irow++) { + row = irow; + if (make[0] == 'O' || model[0] == 'E') { + row = irow * 2 % height + irow / (height/2); + if (row == 1 && data_offset == 0) { + fseek (ifp, 0, SEEK_END); + fseek (ifp, ftell(ifp)/2, SEEK_SET); + getbits(-1); + } + } + for (col=0; col < raw_width; col++) { + i = getbits(12); + if ((unsigned) (col-left_margin) < width) + BAYER(row,col-left_margin) = i; + if (tiff_compress == 34713 && (col % 10) == 9) + getbits(8); + } + } +} + +/* + Figure out if a NEF file is compressed. These fancy heuristics + are only needed for the D100, thanks to a bug in some cameras + that tags all images as "compressed". + */ +int CLASS nikon_is_compressed() +{ + uchar test[256]; + int i; + + if (tiff_compress != 34713) + return 0; + if (strcmp(model,"D100")) + return 1; + fseek (ifp, data_offset, SEEK_SET); + fread (test, 1, 256, ifp); + for (i=15; i < 256; i+=16) + if (test[i]) return 1; + return 0; +} + +/* + Returns 1 for a Coolpix 995, 0 for anything else. + */ +int CLASS nikon_e995() +{ + int i, histo[256]; + const uchar often[] = { 0x00, 0x55, 0xaa, 0xff }; + + memset (histo, 0, sizeof histo); + fseek (ifp, -2000, SEEK_END); + for (i=0; i < 2000; i++) + histo[fgetc(ifp)]++; + for (i=0; i < 4; i++) + if (histo[often[i]] < 200) + return 0; + return 1; +} + +/* + Returns 1 for a Coolpix 2100, 0 for anything else. + */ +int CLASS nikon_e2100() +{ + uchar t[12]; + int i; + + fseek (ifp, 0, SEEK_SET); + for (i=0; i < 1024; i++) { + fread (t, 1, 12, ifp); + if (((t[2] & t[4] & t[7] & t[9]) >> 4 + & t[1] & t[6] & t[8] & t[11] & 3) != 3) + return 0; + } + return 1; +} + +void CLASS nikon_3700() +{ + int i, sum[] = { 0, 0 }; + uchar tail[952]; + + fseek (ifp, -sizeof tail, SEEK_END); + fread (tail, 1, sizeof tail, ifp); + for (i=0; i < sizeof tail; i++) + sum[(i>>2) & 1] += tail[i]; + if (sum[0] > 4*sum[1]) return; + if (sum[1] > 4*sum[0]) { + strcpy (make, "OLYMPUS"); + strcpy (model, "C740UZ"); + return; + } + sum[0] = sum[1] = 0; + for (i=0; i < sizeof tail; i++) + sum[i & 1] += tail[i]; + if (sum[1] > 4*sum[0] || sum[0]+sum[1] > 216000) { + strcpy (make, "PENTAX"); + strcpy (model,"Optio 33WR"); + } else + strcpy (model, "E3200"); +} + +/* + Separates a Minolta DiMAGE Z2 from a Nikon E4300. + */ +int CLASS minolta_z2() +{ + int i; + char tail[424]; + + fseek (ifp, -sizeof tail, SEEK_END); + fread (tail, 1, sizeof tail, ifp); + for (i=0; i < sizeof tail; i++) + if (tail[i]) return 1; + return 0; +} + +/* Here raw_width is in bytes, not pixels. */ +void CLASS nikon_e900_load_raw() +{ + int offset=0, irow, row, col; + + for (irow=0; irow < height; irow++) { + row = irow * 2 % height; + if (row == 1) + offset = - (-offset & -4096); + fseek (ifp, offset, SEEK_SET); + offset += raw_width; + getbits(-1); + for (col=0; col < width; col++) + BAYER(row,col) = getbits(10); + } +} + +void CLASS nikon_e2100_load_raw() +{ + uchar data[3456], *dp; + ushort pixel[2304], *pix; + int row, col; + + for (row=0; row <= height; row+=2) { + if (row == height) { + fseek (ifp, ((width==1616) << 13) - (-ftell(ifp) & -2048), SEEK_SET); + row = 1; + } + fread (data, 1, width*3/2, ifp); + for (dp=data, pix=pixel; pix < pixel+width; dp+=12, pix+=8) { + pix[0] = (dp[2] >> 4) + (dp[ 3] << 4); + pix[1] = (dp[2] << 8) + dp[ 1]; + pix[2] = (dp[7] >> 4) + (dp[ 0] << 4); + pix[3] = (dp[7] << 8) + dp[ 6]; + pix[4] = (dp[4] >> 4) + (dp[ 5] << 4); + pix[5] = (dp[4] << 8) + dp[11]; + pix[6] = (dp[9] >> 4) + (dp[10] << 4); + pix[7] = (dp[9] << 8) + dp[ 8]; + } + for (col=0; col < width; col++) + BAYER(row,col) = (pixel[col] & 0xfff); + } +} + +/* + The Fuji Super CCD is just a Bayer grid rotated 45 degrees. + */ +void CLASS fuji_load_raw() +{ + ushort *pixel; + int row, col, r, c; + + fseek (ifp, (top_margin*raw_width + left_margin) * 2, SEEK_CUR); + pixel = (ushort *) calloc (raw_width, sizeof *pixel); + merror (pixel, "fuji_load_raw()"); + for (row=0; row < raw_height; row++) { + read_shorts (pixel, raw_width); + for (col=0; col < fuji_width << !fuji_layout; col++) { + if (fuji_layout) { + r = fuji_width - 1 - col + (row >> 1); + c = col + ((row+1) >> 1); + } else { + r = fuji_width - 1 + row - (col >> 1); + c = row + ((col+1) >> 1); + } + BAYER(r,c) = pixel[col]; + } + } + free (pixel); +} + +void CLASS jpeg_thumb (FILE *tfp) +{ + char *thumb = (char *) malloc (thumb_length); + merror (thumb, "jpeg_thumb()"); + fread (thumb, 1, thumb_length, ifp); + thumb[0] = 0xff; + fwrite (thumb, 1, thumb_length, tfp); + free (thumb); +} + +void CLASS ppm_thumb (FILE *tfp) +{ + char *thumb = (char *) malloc (thumb_length); + merror (thumb, "ppm_thumb()"); + fprintf (tfp, "P6\n%d %d\n255\n", thumb_width, thumb_height); + fread (thumb, 1, thumb_length, ifp); + fwrite (thumb, 1, thumb_length, tfp); + free (thumb); +} + +void CLASS layer_thumb (FILE *tfp) +{ + int i, c; + char *thumb; + colors = thumb_misc >> 5; + thumb = (char *) malloc (thumb_length*colors); + merror (thumb, "layer_thumb()"); + fprintf (tfp, "P%d\n%d %d\n255\n", + 5 + (thumb_misc >> 6), thumb_width, thumb_height); + fread (thumb, thumb_length, colors, ifp); + for (i=0; i < thumb_length; i++) + FORCC putc (thumb[i+thumb_length*c], tfp); + free (thumb); +} + +void CLASS rollei_thumb (FILE *tfp) +{ + int i, size = thumb_width * thumb_height; + ushort *thumb = (ushort *) calloc (size, 2); + merror (thumb, "rollei_thumb()"); + fprintf (tfp, "P6\n%d %d\n255\n", thumb_width, thumb_height); + read_shorts (thumb, size); + for (i=0; i < size; i++) { + putc (thumb[i] << 3, tfp); + putc (thumb[i] >> 5 << 2, tfp); + putc (thumb[i] >> 11 << 3, tfp); + } + free (thumb); +} + +void CLASS rollei_load_raw() +{ + uchar pixel[10]; + unsigned iten=0, isix, i, buffer=0, row, col, todo[16]; + + isix = raw_width * raw_height * 5 / 8; + while (fread (pixel, 1, 10, ifp) == 10) { + for (i=0; i < 10; i+=2) { + todo[i] = iten++; + todo[i+1] = pixel[i] << 8 | pixel[i+1]; + buffer = pixel[i] >> 2 | buffer << 6; + } + for ( ; i < 16; i+=2) { + todo[i] = isix++; + todo[i+1] = buffer >> (14-i)*5; + } + for (i=0; i < 16; i+=2) { + row = todo[i] / raw_width - top_margin; + col = todo[i] % raw_width - left_margin; + if (row < height && col < width) + BAYER(row,col) = (todo[i+1] & 0x3ff); + } + } + maximum = 0x3ff; +} + +int CLASS bayer (unsigned row, unsigned col) +{ + return (row < height && col < width) ? BAYER(row,col) : 0; +} + +void CLASS phase_one_flat_field (int is_float, int nc) +{ + ushort head[8]; + unsigned wide, y, x, c, rend, cend, row, col; + float *mrow, num, mult[4]; + + read_shorts (head, 8); + wide = head[2] / head[4]; + mrow = (float *) calloc (nc*wide, sizeof *mrow); + merror (mrow, "phase_one_flat_field()"); + for (y=0; y < head[3] / head[5]; y++) { + for (x=0; x < wide; x++) + for (c=0; c < nc; c+=2) { + num = is_float ? getreal(11) : get2()/32768.0; + if (y==0) mrow[c*wide+x] = num; + else mrow[(c+1)*wide+x] = (num - mrow[c*wide+x]) / head[5]; + } + if (y==0) continue; + rend = head[1]-top_margin + y*head[5]; + for (row = rend-head[5]; row < height && row < rend; row++) { + for (x=1; x < wide; x++) { + for (c=0; c < nc; c+=2) { + mult[c] = mrow[c*wide+x-1]; + mult[c+1] = (mrow[c*wide+x] - mult[c]) / head[4]; + } + cend = head[0]-left_margin + x*head[4]; + for (col = cend-head[4]; col < width && col < cend; col++) { + c = nc > 2 ? FC(row,col) : 0; + if (!(c & 1)) { + c = BAYER(row,col) * mult[c]; + BAYER(row,col) = LIM(c,0,65535); + } + for (c=0; c < nc; c+=2) + mult[c] += mult[c+1]; + } + } + for (x=0; x < wide; x++) + for (c=0; c < nc; c+=2) + mrow[c*wide+x] += mrow[(c+1)*wide+x]; + } + } + free (mrow); +} + +void CLASS phase_one_correct() +{ + unsigned entries, tag, data, save, col, row, type; + int len, i, j, k, cip, val[4], dev[4], sum, max; + int head[9], diff, mindiff=INT_MAX, off_412=0; + static const signed char dir[12][2] = + { {-1,-1}, {-1,1}, {1,-1}, {1,1}, {-2,0}, {0,-2}, {0,2}, {2,0}, + {-2,-2}, {-2,2}, {2,-2}, {2,2} }; + float poly[8], num, cfrac, frac, mult[2], *yval[2]; + ushort curve[0x10000], *xval[2]; + + if (shrink || !meta_length) return; + if (verbose) fprintf (stderr, "Phase One correction...\n"); + fseek (ifp, meta_offset, SEEK_SET); + order = get2(); + fseek (ifp, 6, SEEK_CUR); + fseek (ifp, meta_offset+get4(), SEEK_SET); + entries = get4(); get4(); + while (entries--) { + tag = get4(); + len = get4(); + data = get4(); + save = ftell(ifp); + fseek (ifp, meta_offset+data, SEEK_SET); + if (tag == 0x419) { /* Polynomial curve */ + for (get4(), i=0; i < 8; i++) + poly[i] = getreal(11); + poly[3] += (ph1.tag_210 - poly[7]) * poly[6] + 1; + for (i=0; i < 0x10000; i++) { + num = (poly[5]*i + poly[3])*i + poly[1]; + curve[i] = LIM(num,0,65535); + } goto apply; /* apply to right half */ + } else if (tag == 0x41a) { /* Polynomial curve */ + for (i=0; i < 4; i++) + poly[i] = getreal(11); + for (i=0; i < 0x10000; i++) { + for (num=0, j=4; j--; ) + num = num * i + poly[j]; + curve[i] = LIM(num+i,0,65535); + } apply: /* apply to whole image */ + for (row=0; row < height; row++) + for (col = (tag & 1)*ph1.split_col; col < width; col++) + BAYER(row,col) = curve[BAYER(row,col)]; + } else if (tag == 0x400) { /* Sensor defects */ + while ((len -= 8) >= 0) { + col = get2() - left_margin; + row = get2() - top_margin; + type = get2(); get2(); + if (col >= width) continue; + if (type == 131) /* Bad column */ + for (row=0; row < height; row++) + if (FC(row,col) == 1) { + for (sum=i=0; i < 4; i++) + sum += val[i] = bayer (row+dir[i][0], col+dir[i][1]); + for (max=i=0; i < 4; i++) { + dev[i] = abs((val[i] << 2) - sum); + if (dev[max] < dev[i]) max = i; + } + BAYER(row,col) = (sum - val[max])/3.0 + 0.5; + } else { + for (sum=0, i=8; i < 12; i++) + sum += bayer (row+dir[i][0], col+dir[i][1]); + BAYER(row,col) = 0.5 + sum * 0.0732233 + + (bayer(row,col-2) + bayer(row,col+2)) * 0.3535534; + } + else if (type == 129) { /* Bad pixel */ + if (row >= height) continue; + j = (FC(row,col) != 1) * 4; + for (sum=0, i=j; i < j+8; i++) + sum += bayer (row+dir[i][0], col+dir[i][1]); + BAYER(row,col) = (sum + 4) >> 3; + } + } + } else if (tag == 0x401) { /* All-color flat fields */ + phase_one_flat_field (1, 2); + } else if (tag == 0x416 || tag == 0x410) { + phase_one_flat_field (0, 2); + } else if (tag == 0x40b) { /* Red+blue flat field */ + phase_one_flat_field (0, 4); + } else if (tag == 0x412) { + fseek (ifp, 36, SEEK_CUR); + diff = abs (get2() - ph1.tag_21a); + if (mindiff > diff) { + mindiff = diff; + off_412 = ftell(ifp) - 38; + } + } + fseek (ifp, save, SEEK_SET); + } + if (off_412) { + fseek (ifp, off_412, SEEK_SET); + for (i=0; i < 9; i++) head[i] = get4(); + yval[0] = (float *) calloc (head[1]*head[3] + head[2]*head[4], 6); + merror (yval[0], "phase_one_correct()"); + yval[1] = (float *) (yval[0] + head[1]*head[3]); + xval[0] = (ushort *) (yval[1] + head[2]*head[4]); + xval[1] = (ushort *) (xval[0] + head[1]*head[3]); + get2(); + for (i=0; i < 2; i++) + for (j=0; j < head[i+1]*head[i+3]; j++) + yval[i][j] = getreal(11); + for (i=0; i < 2; i++) + for (j=0; j < head[i+1]*head[i+3]; j++) + xval[i][j] = get2(); + for (row=0; row < height; row++) + for (col=0; col < width; col++) { + cfrac = (float) col * head[3] / raw_width; + cfrac -= cip = cfrac; + num = BAYER(row,col) * 0.5; + for (i=cip; i < cip+2; i++) { + for (k=j=0; j < head[1]; j++) + if (num < xval[0][k = head[1]*i+j]) break; + frac = (j == 0 || j == head[1]) ? 0 : + (xval[0][k] - num) / (xval[0][k] - xval[0][k-1]); + mult[i-cip] = yval[0][k-1] * frac + yval[0][k] * (1-frac); + } + i = ((mult[0] * (1-cfrac) + mult[1] * cfrac) + * (row + top_margin) + num) * 2; + BAYER(row,col) = LIM(i,0,65535); + } + free (yval[0]); + } +} + +void CLASS phase_one_load_raw() +{ + int row, col, a, b; + ushort *pixel, akey, bkey, mask; + + fseek (ifp, ph1.key_off, SEEK_SET); + akey = get2(); + bkey = get2(); + mask = ph1.format == 1 ? 0x5555:0x1354; + fseek (ifp, data_offset + top_margin*raw_width*2, SEEK_SET); + pixel = (ushort *) calloc (raw_width, sizeof *pixel); + merror (pixel, "phase_one_load_raw()"); + for (row=0; row < height; row++) { + read_shorts (pixel, raw_width); + for (col=0; col < raw_width; col+=2) { + a = pixel[col+0] ^ akey; + b = pixel[col+1] ^ bkey; + pixel[col+0] = (a & mask) | (b & ~mask); + pixel[col+1] = (b & mask) | (a & ~mask); + } + for (col=0; col < width; col++) + BAYER(row,col) = pixel[col+left_margin]; + } + free (pixel); + phase_one_correct(); +} + +unsigned CLASS ph1_bits (int nbits) +{ + static UINT64 bitbuf=0; + static int vbits=0; + + if (nbits == 0) + return bitbuf = vbits = 0; + if (vbits < nbits) { + bitbuf = bitbuf << 32 | (unsigned) get4(); + vbits += 32; + } + vbits -= nbits; + return bitbuf << (64 - nbits - vbits) >> (64 - nbits); +} + +void CLASS phase_one_load_raw_c() +{ + static const int length[] = { 8,7,6,9,11,10,5,12,14,13 }; + int *offset, len[2], pred[2], row, col, i, j; + ushort *pixel; + short (*black)[2]; + + pixel = (ushort *) calloc (raw_width + raw_height*4, 2); + merror (pixel, "phase_one_load_raw_c()"); + offset = (int *) (pixel + raw_width); + fseek (ifp, strip_offset, SEEK_SET); + for (row=0; row < raw_height; row++) + offset[row] = get4(); + black = (short (*)[2]) offset + raw_height; + fseek (ifp, ph1.black_off, SEEK_SET); + if (ph1.black_off) + read_shorts ((ushort *) black[0], raw_height*2); + for (i=0; i < 256; i++) + curve[i] = i*i / 3.969 + 0.5; + for (row=0; row < raw_height; row++) { + fseek (ifp, data_offset + offset[row], SEEK_SET); + ph1_bits(0); + pred[0] = pred[1] = 0; + for (col=0; col < raw_width; col++) { + if (col >= (raw_width & -8)) + len[0] = len[1] = 14; + else if ((col & 7) == 0) + for (i=0; i < 2; i++) { + for (j=0; j < 5 && !ph1_bits(1); j++); + if (j--) len[i] = length[j*2 + ph1_bits(1)]; + } + if ((i = len[col & 1]) == 14) + pixel[col] = pred[col & 1] = ph1_bits(16); + else + pixel[col] = pred[col & 1] += ph1_bits(i) + 1 - (1 << (i - 1)); + if (ph1.format == 5 && pixel[col] < 256) + pixel[col] = curve[pixel[col]]; + } + if ((unsigned) (row-top_margin) < height) + for (col=0; col < width; col++) { + i = (pixel[col+left_margin] << 2) + - ph1.black + black[row][col >= ph1.split_col]; + if (i > 0) BAYER(row-top_margin,col) = i; + } + } + free (pixel); + phase_one_correct(); + maximum = 0xfffc - ph1.black; +} + +void CLASS leaf_hdr_load_raw() +{ + ushort *pixel; + unsigned tile=0, r, c, row, col; + + pixel = (ushort *) calloc (raw_width, sizeof *pixel); + merror (pixel, "leaf_hdr_load_raw()"); + for (c=0; c < tiff_samples; c++) { + if (filters && c != MIN(1,tiff_samples-1)) continue; + for (r=0; r < raw_height; r++) { + if (r % tile_length == 0) { + fseek (ifp, data_offset + 4*tile++, SEEK_SET); + fseek (ifp, get4() + 2*left_margin, SEEK_SET); + } + read_shorts (pixel, raw_width); + if ((row = r - top_margin) >= height) continue; + for (col=0; col < width; col++) + if (filters) BAYER(row,col) = pixel[col]; + else image[row*width+col][c] = pixel[col]; + } + } + free (pixel); + if (!filters) { + maximum = 0xffff; + raw_color = 1; + } +} + +void CLASS imacon_full_load_raw() +{ + int row, col; + + for (row=0; row < height; row++) + for (col=0; col < width; col++) + read_shorts (image[row*width+col], 3); +} + +/* Here raw_width is in bytes, not pixels. */ +void CLASS packed_12_load_raw() +{ + int row, col; + + getbits(-1); + for (row=0; row < height; row++) { + for (col=0; col < width; col++) + BAYER(row,col) = getbits(12); + for (col = width*3/2; col < raw_width; col++) + getbits(8); + } +} + +void CLASS unpacked_load_raw() +{ + ushort *pixel; + int row, col; + + fseek (ifp, (top_margin*raw_width + left_margin) * 2, SEEK_CUR); + pixel = (ushort *) calloc (raw_width, sizeof *pixel); + merror (pixel, "unpacked_load_raw()"); + for (row=0; row < height; row++) { + read_shorts (pixel, raw_width); + for (col=0; col < width; col++) + BAYER2(row,col) = pixel[col]; + } + free (pixel); +} + +void CLASS olympus_e300_load_raw() +{ + uchar *data, *dp; + ushort *pixel, *pix; + int dwide, row, col, bls=width, ble=raw_width; + + if (raw_width == 3360) bls += 4; + if (raw_width == 3280) ble = bls + 8; + dwide = raw_width * 16 / 10; + data = (uchar *) malloc (dwide + raw_width*2); + merror (data, "olympus_e300_load_raw()"); + pixel = (ushort *) (data + dwide); + for (row=0; row < height; row++) { + fread (data, 1, dwide, ifp); + for (dp=data, pix=pixel; pix < pixel+raw_width; dp+=3, pix+=2) { + if (((dp-data) & 15) == 15) dp++; + pix[0] = dp[1] << 8 | dp[0]; + pix[1] = dp[2] << 4 | dp[1] >> 4; + } + for (col=0; col < width; col++) + BAYER(row,col) = (pixel[col] & 0xfff); + for (col=bls; col < ble; col++) + black += pixel[col] & 0xfff; + } + if (ble > bls) black /= (ble - bls) * height; + free (data); + maximum = 0xfff; +} + +void CLASS olympus_cseries_load_raw() +{ + int irow, row, col; + + for (irow=0; irow < height; irow++) { + row = irow * 2 % height + irow / (height/2); + if (row < 2) { + fseek (ifp, data_offset - row*(-width*height*3/4 & -2048), SEEK_SET); + getbits(-1); + } + for (col=0; col < width; col++) + BAYER(row,col) = getbits(12); + } +} + +void CLASS minolta_rd175_load_raw() +{ + uchar pixel[768]; + unsigned irow, box, row, col; + + for (irow=0; irow < 1481; irow++) { + fread (pixel, 1, 768, ifp); + box = irow / 82; + row = irow % 82 * 12 + ((box < 12) ? box | 1 : (box-12)*2); + switch (irow) { + case 1477: case 1479: continue; + case 1476: row = 984; break; + case 1480: row = 985; break; + case 1478: row = 985; box = 1; + } + if ((box < 12) && (box & 1)) { + for (col=0; col < 1533; col++, row ^= 1) + if (col != 1) BAYER(row,col) = (col+1) & 2 ? + pixel[col/2-1] + pixel[col/2+1] : pixel[col/2] << 1; + BAYER(row,1) = pixel[1] << 1; + BAYER(row,1533) = pixel[765] << 1; + } else + for (col=row & 1; col < 1534; col+=2) + BAYER(row,col) = pixel[col/2] << 1; + } + maximum = 0xff << 1; +} + +void CLASS eight_bit_load_raw() +{ + uchar *pixel; + int row, col; + + pixel = (uchar *) calloc (raw_width, sizeof *pixel); + merror (pixel, "eight_bit_load_raw()"); + for (row=0; row < height; row++) { + fread (pixel, 1, raw_width, ifp); + for (col=0; col < width; col++) + BAYER(row,col) = pixel[col]; + } + free (pixel); + maximum = 0xff; +} + +void CLASS casio_qv5700_load_raw() +{ + uchar data[3232], *dp; + ushort pixel[2576], *pix; + int row, col; + + for (row=0; row < height; row++) { + fread (data, 1, 3232, ifp); + for (dp=data, pix=pixel; dp < data+3220; dp+=5, pix+=4) { + pix[0] = (dp[0] << 2) + (dp[1] >> 6); + pix[1] = (dp[1] << 4) + (dp[2] >> 4); + pix[2] = (dp[2] << 6) + (dp[3] >> 2); + pix[3] = (dp[3] << 8) + (dp[4] ); + } + for (col=0; col < width; col++) + BAYER(row,col) = (pixel[col] & 0x3ff); + } + maximum = 0x3fc; +} + +void CLASS nucore_load_raw() +{ + ushort *pixel; + int irow, row, col; + + pixel = (ushort *) calloc (width, 2); + merror (pixel, "nucore_load_raw()"); + for (irow=0; irow < height; irow++) { + read_shorts (pixel, width); + row = irow/2 + height/2 * (irow & 1); + for (col=0; col < width; col++) + BAYER(row,col) = pixel[col]; + } + free (pixel); +} + +const int * CLASS make_decoder_int (const int *source, int level) +{ + struct decode *cur; + + cur = free_decode++; + if (level < source[0]) { + cur->branch[0] = free_decode; + source = make_decoder_int (source, level+1); + cur->branch[1] = free_decode; + source = make_decoder_int (source, level+1); + } else { + cur->leaf = source[1]; + source += 2; + } + return source; +} + +int CLASS radc_token (int tree) +{ + int t; + static struct decode *dstart[18], *dindex; + static const int *s, source[] = { + 1,1, 2,3, 3,4, 4,2, 5,7, 6,5, 7,6, 7,8, + 1,0, 2,1, 3,3, 4,4, 5,2, 6,7, 7,6, 8,5, 8,8, + 2,1, 2,3, 3,0, 3,2, 3,4, 4,6, 5,5, 6,7, 6,8, + 2,0, 2,1, 2,3, 3,2, 4,4, 5,6, 6,7, 7,5, 7,8, + 2,1, 2,4, 3,0, 3,2, 3,3, 4,7, 5,5, 6,6, 6,8, + 2,3, 3,1, 3,2, 3,4, 3,5, 3,6, 4,7, 5,0, 5,8, + 2,3, 2,6, 3,0, 3,1, 4,4, 4,5, 4,7, 5,2, 5,8, + 2,4, 2,7, 3,3, 3,6, 4,1, 4,2, 4,5, 5,0, 5,8, + 2,6, 3,1, 3,3, 3,5, 3,7, 3,8, 4,0, 5,2, 5,4, + 2,0, 2,1, 3,2, 3,3, 4,4, 4,5, 5,6, 5,7, 4,8, + 1,0, 2,2, 2,-2, + 1,-3, 1,3, + 2,-17, 2,-5, 2,5, 2,17, + 2,-7, 2,2, 2,9, 2,18, + 2,-18, 2,-9, 2,-2, 2,7, + 2,-28, 2,28, 3,-49, 3,-9, 3,9, 4,49, 5,-79, 5,79, + 2,-1, 2,13, 2,26, 3,39, 4,-16, 5,55, 6,-37, 6,76, + 2,-26, 2,-13, 2,1, 3,-39, 4,16, 5,-55, 6,-76, 6,37 + }; + + if (free_decode == first_decode) + for (s=source, t=0; t < 18; t++) { + dstart[t] = free_decode; + s = make_decoder_int (s, 0); + } + if (tree == 18) { + if (kodak_cbpp == 243) + return (getbits(6) << 2) + 2; /* most DC50 photos */ + else + return (getbits(5) << 3) + 4; /* DC40, Fotoman Pixtura */ + } + for (dindex = dstart[tree]; dindex->branch[0]; ) + dindex = dindex->branch[getbits(1)]; + return dindex->leaf; +} + +#define FORYX for (y=1; y < 3; y++) for (x=col+1; x >= col; x--) + +#define PREDICTOR (c ? (buf[c][y-1][x] + buf[c][y][x+1]) / 2 \ +: (buf[c][y-1][x+1] + 2*buf[c][y-1][x] + buf[c][y][x+1]) / 4) + +void CLASS kodak_radc_load_raw() +{ + int row, col, tree, nreps, rep, step, i, c, s, r, x, y, val; + short last[3] = { 16,16,16 }, mul[3], buf[3][3][386]; + + init_decoder(); + getbits(-1); + for (i=0; i < sizeof(buf)/sizeof(short); i++) + buf[0][0][i] = 2048; + for (row=0; row < height; row+=4) { + FORC3 mul[c] = getbits(6); + FORC3 { + val = ((0x1000000/last[c] + 0x7ff) >> 12) * mul[c]; + s = val > 65564 ? 10:12; + x = ~(-1 << (s-1)); + val <<= 12-s; + for (i=0; i < sizeof(buf[0])/sizeof(short); i++) + buf[c][0][i] = (buf[c][0][i] * val + x) >> s; + last[c] = mul[c]; + for (r=0; r <= !c; r++) { + buf[c][1][width/2] = buf[c][2][width/2] = mul[c] << 7; + for (tree=1, col=width/2; col > 0; ) { + if ((tree = radc_token(tree))) { + col -= 2; + if (tree == 8) + FORYX buf[c][y][x] = radc_token(tree+10) * mul[c]; + else + FORYX buf[c][y][x] = radc_token(tree+10) * 16 + PREDICTOR; + } else + do { + nreps = (col > 2) ? radc_token(9) + 1 : 1; + for (rep=0; rep < 8 && rep < nreps && col > 0; rep++) { + col -= 2; + FORYX buf[c][y][x] = PREDICTOR; + if (rep & 1) { + step = radc_token(10) << 4; + FORYX buf[c][y][x] += step; + } + } + } while (nreps == 9); + } + for (y=0; y < 2; y++) + for (x=0; x < width/2; x++) { + val = (buf[c][y+1][x] << 4) / mul[c]; + if (val < 0) val = 0; + if (c) + BAYER(row+y*2+c-1,x*2+2-c) = val; + else + BAYER(row+r*2+y,x*2+y) = val; + } + memcpy (buf[c][0]+!c, buf[c][2], sizeof buf[c][0]-2*!c); + } + } + for (y=row; y < row+4; y++) + for (x=0; x < width; x++) + if ((x+y) & 1) { + val = (BAYER(y,x)-2048)*2 + (BAYER(y,x-1)+BAYER(y,x+1))/2; + if (val < 0) val = 0; + BAYER(y,x) = val; + } + } + maximum = 10000; +} + +#undef FORYX +#undef PREDICTOR + +#ifdef NO_JPEG +void CLASS kodak_jpeg_load_raw() {} +#else + +METHODDEF(boolean) +fill_input_buffer (j_decompress_ptr cinfo) +{ + static uchar jpeg_buffer[4096]; + size_t nbytes; + + nbytes = fread (jpeg_buffer, 1, 4096, ifp); + swab (jpeg_buffer, jpeg_buffer, nbytes); + cinfo->src->next_input_byte = jpeg_buffer; + cinfo->src->bytes_in_buffer = nbytes; + return TRUE; +} + +void CLASS kodak_jpeg_load_raw() +{ + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + JSAMPARRAY buf; + JSAMPLE (*pixel)[3]; + int row, col; + + cinfo.err = jpeg_std_error (&jerr); + jpeg_create_decompress (&cinfo); + jpeg_stdio_src (&cinfo, ifp); + cinfo.src->fill_input_buffer = fill_input_buffer; + jpeg_read_header (&cinfo, TRUE); + jpeg_start_decompress (&cinfo); + if ((cinfo.output_width != width ) || + (cinfo.output_height*2 != height ) || + (cinfo.output_components != 3 )) { + fprintf (stderr, "%s: incorrect JPEG dimensions\n", ifname); + jpeg_destroy_decompress (&cinfo); + longjmp (failure, 3); + } + buf = (*cinfo.mem->alloc_sarray) + ((j_common_ptr) &cinfo, JPOOL_IMAGE, width*3, 1); + + while (cinfo.output_scanline < cinfo.output_height) { + row = cinfo.output_scanline * 2; + jpeg_read_scanlines (&cinfo, buf, 1); + pixel = (JSAMPLE (*)[3]) buf[0]; + for (col=0; col < width; col+=2) { + BAYER(row+0,col+0) = pixel[col+0][1] << 1; + BAYER(row+1,col+1) = pixel[col+1][1] << 1; + BAYER(row+0,col+1) = pixel[col][0] + pixel[col+1][0]; + BAYER(row+1,col+0) = pixel[col][2] + pixel[col+1][2]; + } + } + jpeg_finish_decompress (&cinfo); + jpeg_destroy_decompress (&cinfo); + maximum = 0xff << 1; +} +#endif + +void CLASS kodak_dc120_load_raw() +{ + static const int mul[4] = { 162, 192, 187, 92 }; + static const int add[4] = { 0, 636, 424, 212 }; + uchar pixel[848]; + int row, shift, col; + + for (row=0; row < height; row++) { + fread (pixel, 848, 1, ifp); + shift = row * mul[row & 3] + add[row & 3]; + for (col=0; col < width; col++) + BAYER(row,col) = (ushort) pixel[(col + shift) % 848]; + } + maximum = 0xff; +} + +void CLASS kodak_easy_load_raw() +{ + uchar *pixel; + unsigned row, col, val; + + if (raw_width > width) + black = 0; + pixel = (uchar *) calloc (raw_width, sizeof *pixel); + merror (pixel, "kodak_easy_load_raw()"); + for (row=0; row < height; row++) { + fread (pixel, 1, raw_width, ifp); + for (col=0; col < raw_width; col++) { + val = curve[pixel[col]]; + if ((unsigned) (col-left_margin) < width) + BAYER(row,col-left_margin) = val; + else black += val; + } + } + free (pixel); + if (raw_width > width) + black /= (raw_width - width) * height; + if (!strncmp(model,"DC2",3)) + black = 0; + maximum = curve[0xff]; +} + +void CLASS kodak_262_load_raw() +{ + static const uchar kodak_tree[2][26] = + { { 0,1,5,1,1,2,0,0,0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7,8,9 }, + { 0,3,1,1,1,1,1,2,0,0,0,0,0,0,0,0, 0,1,2,3,4,5,6,7,8,9 } }; + struct decode *decode[2]; + uchar *pixel; + int *strip, ns, i, row, col, chess, pi=0, pi1, pi2, pred, val; + + init_decoder(); + for (i=0; i < 2; i++) { + decode[i] = free_decode; + make_decoder (kodak_tree[i], 0); + } + ns = (raw_height+63) >> 5; + pixel = (uchar *) malloc (raw_width*32 + ns*4); + merror (pixel, "kodak_262_load_raw()"); + strip = (int *) (pixel + raw_width*32); + order = 0x4d4d; + for (i=0; i < ns; i++) + strip[i] = get4(); + for (row=0; row < raw_height; row++) { + if ((row & 31) == 0) { + fseek (ifp, strip[row >> 5], SEEK_SET); + getbits(-1); + pi = 0; + } + for (col=0; col < raw_width; col++) { + chess = (row + col) & 1; + pi1 = chess ? pi-2 : pi-raw_width-1; + pi2 = chess ? pi-2*raw_width : pi-raw_width+1; + if (col <= chess) pi1 = -1; + if (pi1 < 0) pi1 = pi2; + if (pi2 < 0) pi2 = pi1; + if (pi1 < 0 && col > 1) pi1 = pi2 = pi-2; + pred = (pi1 < 0) ? 0 : (pixel[pi1] + pixel[pi2]) >> 1; + pixel[pi] = pred + ljpeg_diff (decode[chess]); + val = curve[pixel[pi++]]; + if ((unsigned) (col-left_margin) < width) + BAYER(row,col-left_margin) = val; + else black += val; + } + } + free (pixel); + if (raw_width > width) + black /= (raw_width - width) * height; +} + +int CLASS kodak_65000_decode (short *out, int bsize) +{ + uchar c, blen[768]; + ushort raw[6]; + INT64 bitbuf=0; + int save, bits=0, i, j, len, diff; + + save = ftell(ifp); + bsize = (bsize + 3) & -4; + for (i=0; i < bsize; i+=2) { + c = fgetc(ifp); + if ((blen[i ] = c & 15) > 12 || + (blen[i+1] = c >> 4) > 12 ) { + fseek (ifp, save, SEEK_SET); + for (i=0; i < bsize; i+=8) { + read_shorts (raw, 6); + out[i ] = raw[0] >> 12 << 8 | raw[2] >> 12 << 4 | raw[4] >> 12; + out[i+1] = raw[1] >> 12 << 8 | raw[3] >> 12 << 4 | raw[5] >> 12; + for (j=0; j < 6; j++) + out[i+2+j] = raw[j] & 0xfff; + } + return 1; + } + } + if ((bsize & 7) == 4) { + bitbuf = fgetc(ifp) << 8; + bitbuf += fgetc(ifp); + bits = 16; + } + for (i=0; i < bsize; i++) { + len = blen[i]; + if (bits < len) { + for (j=0; j < 32; j+=8) + bitbuf += (INT64) fgetc(ifp) << (bits+(j^8)); + bits += 32; + } + diff = bitbuf & (0xffff >> (16-len)); + bitbuf >>= len; + bits -= len; + if ((diff & (1 << (len-1))) == 0) + diff -= (1 << len) - 1; + out[i] = diff; + } + return 0; +} + +void CLASS kodak_65000_load_raw() +{ + short buf[256]; + int row, col, len, pred[2], ret, i; + + for (row=0; row < height; row++) + for (col=0; col < width; col+=256) { + pred[0] = pred[1] = 0; + len = MIN (256, width-col); + ret = kodak_65000_decode (buf, len); + for (i=0; i < len; i++) + BAYER(row,col+i) = curve[ret ? buf[i] : (pred[i & 1] += buf[i])]; + } +} + +void CLASS kodak_ycbcr_load_raw() +{ + short buf[384], *bp; + int row, col, len, c, i, j, k, y[2][2], cb, cr, rgb[3]; + ushort *ip; + + for (row=0; row < height; row+=2) + for (col=0; col < width; col+=128) { + len = MIN (128, width-col); + kodak_65000_decode (buf, len*3); + y[0][1] = y[1][1] = cb = cr = 0; + for (bp=buf, i=0; i < len; i+=2, bp+=2) { + cb += bp[4]; + cr += bp[5]; + rgb[1] = -((cb + cr + 2) >> 2); + rgb[2] = rgb[1] + cb; + rgb[0] = rgb[1] + cr; + for (j=0; j < 2; j++) + for (k=0; k < 2; k++) { + y[j][k] = y[j][k^1] + *bp++; + ip = image[(row+j)*width + col+i+k]; + FORC3 ip[c] = curve[LIM(y[j][k]+rgb[c], 0, 0xfff)]; + } + } + } +} + +void CLASS kodak_rgb_load_raw() +{ + short buf[768], *bp; + int row, col, len, c, i, rgb[3]; + ushort *ip=image[0]; + + for (row=0; row < height; row++) + for (col=0; col < width; col+=256) { + len = MIN (256, width-col); + kodak_65000_decode (buf, len*3); + memset (rgb, 0, sizeof rgb); + for (bp=buf, i=0; i < len; i++, ip+=4) + FORC3 ip[c] = (rgb[c] += *bp++) & 0xfff; + } +} + +void CLASS kodak_thumb_load_raw() +{ + int row, col; + colors = thumb_misc >> 5; + for (row=0; row < height; row++) + for (col=0; col < width; col++) + read_shorts (image[row*width+col], colors); + maximum = (1 << (thumb_misc & 31)) - 1; +} + +void CLASS sony_decrypt (unsigned *data, int len, int start, int key) +{ + static unsigned pad[128], p; + + if (start) { + for (p=0; p < 4; p++) + pad[p] = key = key * 48828125 + 1; + pad[3] = pad[3] << 1 | (pad[0]^pad[2]) >> 31; + for (p=4; p < 127; p++) + pad[p] = (pad[p-4]^pad[p-2]) << 1 | (pad[p-3]^pad[p-1]) >> 31; + for (p=0; p < 127; p++) + pad[p] = htonl(pad[p]); + } + while (len--) + *data++ ^= pad[p++ & 127] = pad[(p+1) & 127] ^ pad[(p+65) & 127]; +} + +void CLASS sony_load_raw() +{ + uchar head[40]; + ushort *pixel; + unsigned i, key, row, col; + + fseek (ifp, 200896, SEEK_SET); + fseek (ifp, (unsigned) fgetc(ifp)*4 - 1, SEEK_CUR); + order = 0x4d4d; + key = get4(); + fseek (ifp, 164600, SEEK_SET); + fread (head, 1, 40, ifp); + sony_decrypt ((unsigned int *) head, 10, 1, key); + for (i=26; i-- > 22; ) + key = key << 8 | head[i]; + fseek (ifp, data_offset, SEEK_SET); + pixel = (ushort *) calloc (raw_width, sizeof *pixel); + merror (pixel, "sony_load_raw()"); + for (row=0; row < height; row++) { + fread (pixel, 2, raw_width, ifp); + sony_decrypt ((unsigned int *) pixel, raw_width/2, !row, key); + for (col=9; col < left_margin; col++) + black += ntohs(pixel[col]); + for (col=0; col < width; col++) + BAYER(row,col) = ntohs(pixel[col+left_margin]); + } + free (pixel); + if (left_margin > 9) + black /= (left_margin-9) * height; + maximum = 0x3ff0; +} + +void CLASS sony_arw_load_raw() +{ + int col, row, len, diff, sum=0; + + getbits(-1); + for (col = raw_width; col--; ) + for (row=0; row < raw_height+1; row+=2) { + if (row == raw_height) row = 1; + len = 4 - getbits(2); + if (len == 3 && getbits(1)) len = 0; + if (len == 4) + while (len < 17 && !getbits(1)) len++; + diff = getbits(len); + if ((diff & (1 << (len-1))) == 0) + diff -= (1 << len) - 1; + sum += diff; + if (row < height) BAYER(row,col) = sum; + } +} + +#define HOLE(row) ((holes >> (((row) - raw_height) & 7)) & 1) + +/* Kudos to Rich Taylor for figuring out SMaL's compression algorithm. */ +void CLASS smal_decode_segment (unsigned seg[2][2], int holes) +{ + uchar hist[3][13] = { + { 7, 7, 0, 0, 63, 55, 47, 39, 31, 23, 15, 7, 0 }, + { 7, 7, 0, 0, 63, 55, 47, 39, 31, 23, 15, 7, 0 }, + { 3, 3, 0, 0, 63, 47, 31, 15, 0 } }; + int low, high=0xff, carry=0, nbits=8; + int s, count, bin, next, i, sym[3]; + uchar diff, pred[]={0,0}; + ushort data=0, range=0; + unsigned pix, row, col; + + fseek (ifp, seg[0][1]+1, SEEK_SET); + getbits(-1); + for (pix=seg[0][0]; pix < seg[1][0]; pix++) { + for (s=0; s < 3; s++) { + data = data << nbits | getbits(nbits); + if (carry < 0) + carry = (nbits += carry+1) < 1 ? nbits-1 : 0; + while (--nbits >= 0) + if ((data >> nbits & 0xff) == 0xff) break; + if (nbits > 0) + data = ((data & ((1 << (nbits-1)) - 1)) << 1) | + ((data + (((data & (1 << (nbits-1)))) << 1)) & (-1 << nbits)); + if (nbits >= 0) { + data += getbits(1); + carry = nbits - 8; + } + count = ((((data-range+1) & 0xffff) << 2) - 1) / (high >> 4); + for (bin=0; hist[s][bin+5] > count; bin++); + low = hist[s][bin+5] * (high >> 4) >> 2; + if (bin) high = hist[s][bin+4] * (high >> 4) >> 2; + high -= low; + for (nbits=0; high << nbits < 128; nbits++); + range = (range+low) << nbits; + high <<= nbits; + next = hist[s][1]; + if (++hist[s][2] > hist[s][3]) { + next = (next+1) & hist[s][0]; + hist[s][3] = (hist[s][next+4] - hist[s][next+5]) >> 2; + hist[s][2] = 1; + } + if (hist[s][hist[s][1]+4] - hist[s][hist[s][1]+5] > 1) { + if (bin < hist[s][1]) + for (i=bin; i < hist[s][1]; i++) hist[s][i+5]--; + else if (next <= bin) + for (i=hist[s][1]; i < bin; i++) hist[s][i+5]++; + } + hist[s][1] = next; + sym[s] = bin; + } + diff = sym[2] << 5 | sym[1] << 2 | (sym[0] & 3); + if (sym[0] & 4) + diff = diff ? -diff : 0x80; + if (ftell(ifp) + 12 >= seg[1][1]) + diff = 0; + pred[pix & 1] += diff; + row = pix / raw_width - top_margin; + col = pix % raw_width - left_margin; + if (row < height && col < width) + BAYER(row,col) = pred[pix & 1]; + if (!(pix & 1) && HOLE(row)) pix += 2; + } + maximum = 0xff; +} + +void CLASS smal_v6_load_raw() +{ + unsigned seg[2][2]; + + fseek (ifp, 16, SEEK_SET); + seg[0][0] = 0; + seg[0][1] = get2(); + seg[1][0] = raw_width * raw_height; + seg[1][1] = INT_MAX; + smal_decode_segment (seg, 0); + use_gamma = 0; +} + +int CLASS median4 (int *p) +{ + int min, max, sum, i; + + min = max = sum = p[0]; + for (i=1; i < 4; i++) { + sum += p[i]; + if (min > p[i]) min = p[i]; + if (max < p[i]) max = p[i]; + } + return (sum - min - max) >> 1; +} + +void CLASS fill_holes (int holes) +{ + int row, col, val[4]; + + for (row=2; row < height-2; row++) { + if (!HOLE(row)) continue; + for (col=1; col < width-1; col+=4) { + val[0] = BAYER(row-1,col-1); + val[1] = BAYER(row-1,col+1); + val[2] = BAYER(row+1,col-1); + val[3] = BAYER(row+1,col+1); + BAYER(row,col) = median4(val); + } + for (col=2; col < width-2; col+=4) + if (HOLE(row-2) || HOLE(row+2)) + BAYER(row,col) = (BAYER(row,col-2) + BAYER(row,col+2)) >> 1; + else { + val[0] = BAYER(row,col-2); + val[1] = BAYER(row,col+2); + val[2] = BAYER(row-2,col); + val[3] = BAYER(row+2,col); + BAYER(row,col) = median4(val); + } + } +} + +void CLASS smal_v9_load_raw() +{ + unsigned seg[256][2], offset, nseg, holes, i; + + fseek (ifp, 67, SEEK_SET); + offset = get4(); + nseg = fgetc(ifp); + fseek (ifp, offset, SEEK_SET); + for (i=0; i < nseg*2; i++) + seg[0][i] = get4() + data_offset*(i & 1); + fseek (ifp, 78, SEEK_SET); + holes = fgetc(ifp); + fseek (ifp, 88, SEEK_SET); + seg[nseg][0] = raw_height * raw_width; + seg[nseg][1] = get4() + data_offset; + for (i=0; i < nseg; i++) + smal_decode_segment (seg+i, holes); + if (holes) fill_holes (holes); +} + +/* BEGIN GPL BLOCK */ + +void CLASS foveon_decoder (unsigned size, unsigned code) +{ + static unsigned huff[1024]; + struct decode *cur; + int i, len; + + if (!code) { + for (i=0; i < size; i++) + huff[i] = get4(); + init_decoder(); + } + cur = free_decode++; + if (free_decode > first_decode+2048) { + fprintf (stderr, "%s: decoder table overflow\n", ifname); + longjmp (failure, 2); + } + if (code) + for (i=0; i < size; i++) + if (huff[i] == code) { + cur->leaf = i; + return; + } + if ((len = code >> 27) > 26) return; + code = (len+1) << 27 | (code & 0x3ffffff) << 1; + + cur->branch[0] = free_decode; + foveon_decoder (size, code); + cur->branch[1] = free_decode; + foveon_decoder (size, code+1); +} + +void CLASS foveon_thumb (FILE *tfp) +{ + int bwide, row, col, bit=-1, c, i; + char *buf; + struct decode *dindex; + short pred[3]; + unsigned bitbuf=0; + + bwide = get4(); + fprintf (tfp, "P6\n%d %d\n255\n", thumb_width, thumb_height); + if (bwide > 0) { + buf = (char *) malloc (bwide); + merror (buf, "foveon_thumb()"); + for (row=0; row < thumb_height; row++) { + fread (buf, 1, bwide, ifp); + fwrite (buf, 3, thumb_width, tfp); + } + free (buf); + return; + } + foveon_decoder (256, 0); + + for (row=0; row < thumb_height; row++) { + memset (pred, 0, sizeof pred); + if (!bit) get4(); + for (col=bit=0; col < thumb_width; col++) + FORC3 { + for (dindex=first_decode; dindex->branch[0]; ) { + if ((bit = (bit-1) & 31) == 31) + for (i=0; i < 4; i++) + bitbuf = (bitbuf << 8) + fgetc(ifp); + dindex = dindex->branch[bitbuf >> bit & 1]; + } + pred[c] += dindex->leaf; + fputc (pred[c], tfp); + } + } +} + +void CLASS foveon_load_camf() +{ + unsigned key, i, val; + + fseek (ifp, meta_offset, SEEK_SET); + key = get4(); + fread (meta_data, 1, meta_length, ifp); + for (i=0; i < meta_length; i++) { + key = (key * 1597 + 51749) % 244944; + val = key * (INT64) 301593171 >> 24; + meta_data[i] ^= ((((key << 8) - val) >> 1) + val) >> 17; + } +} + +void CLASS foveon_load_raw() +{ + struct decode *dindex; + short diff[1024], pred[3]; + unsigned bitbuf=0; + int fixed, row, col, bit=-1, c, i; + + fixed = get4(); + read_shorts ((ushort *) diff, 1024); + if (!fixed) foveon_decoder (1024, 0); + + for (row=0; row < height; row++) { + memset (pred, 0, sizeof pred); + if (!bit && !fixed) get4(); + for (col=bit=0; col < width; col++) { + if (fixed) { + bitbuf = get4(); + FORC3 pred[2-c] += diff[bitbuf >> c*10 & 0x3ff]; + } + else FORC3 { + for (dindex=first_decode; dindex->branch[0]; ) { + if ((bit = (bit-1) & 31) == 31) + for (i=0; i < 4; i++) + bitbuf = (bitbuf << 8) + fgetc(ifp); + dindex = dindex->branch[bitbuf >> bit & 1]; + } + pred[c] += diff[dindex->leaf]; + } + FORC3 image[row*width+col][c] = pred[c]; + } + } + if (document_mode) + for (i=0; i < height*width*4; i++) + if ((short) image[0][i] < 0) image[0][i] = 0; + foveon_load_camf(); +} + +const char * CLASS foveon_camf_param (const char *block, const char *param) +{ + unsigned idx, num; + char *pos, *cp, *dp; + + for (idx=0; idx < meta_length; idx += sget4(pos+8)) { + pos = meta_data + idx; + if (strncmp (pos, "CMb", 3)) break; + if (pos[3] != 'P') continue; + if (strcmp (block, pos+sget4(pos+12))) continue; + cp = pos + sget4(pos+16); + num = sget4(cp); + dp = pos + sget4(cp+4); + while (num--) { + cp += 8; + if (!strcmp (param, dp+sget4(cp))) + return dp+sget4(cp+4); + } + } + return NULL; +} + +void * CLASS foveon_camf_matrix (int dim[3], const char *name) +{ + unsigned i, idx, type, ndim, size, *mat; + char *pos, *cp, *dp; + + for (idx=0; idx < meta_length; idx += sget4(pos+8)) { + pos = meta_data + idx; + if (strncmp (pos, "CMb", 3)) break; + if (pos[3] != 'M') continue; + if (strcmp (name, pos+sget4(pos+12))) continue; + dim[0] = dim[1] = dim[2] = 1; + cp = pos + sget4(pos+16); + type = sget4(cp); + if ((ndim = sget4(cp+4)) > 3) break; + dp = pos + sget4(cp+8); + for (i=ndim; i--; ) { + cp += 12; + dim[i] = sget4(cp); + } + if ((size = dim[0]*dim[1]*dim[2]) > meta_length/4) break; + mat = (unsigned *) malloc (size * 4); + merror (mat, "foveon_camf_matrix()"); + for (i=0; i < size; i++) + if (type && type != 6) + mat[i] = sget4(dp + i*4); + else + mat[i] = sget4(dp + i*2) & 0xffff; + return mat; + } + fprintf (stderr, "%s: \"%s\" matrix not found!\n", ifname, name); + return NULL; +} + +int CLASS foveon_fixed (void *ptr, int size, const char *name) +{ + void *dp; + int dim[3]; + + dp = foveon_camf_matrix (dim, name); + if (!dp) return 0; + memcpy (ptr, dp, size*4); + free (dp); + return 1; +} + +float CLASS foveon_avg (short *pix, int range[2], float cfilt) +{ + int i; + float val, min=FLT_MAX, max=-FLT_MAX, sum=0; + + for (i=range[0]; i <= range[1]; i++) { + sum += val = pix[i*4] + (pix[i*4]-pix[(i-1)*4]) * cfilt; + if (min > val) min = val; + if (max < val) max = val; + } + return (sum - min - max) / (range[1] - range[0] - 1); +} + +short * CLASS foveon_make_curve (double max, double mul, double filt) +{ + short *curve; + int i, size; + double x; + + if (!filt) filt = 0.8; + size = 4*M_PI*max / filt; + curve = (short *) calloc (size+1, sizeof *curve); + merror (curve, "foveon_make_curve()"); + curve[0] = size; + for (i=0; i < size; i++) { + x = i*filt/max/4; + curve[i+1] = (cos(x)+1)/2 * tanh(i*filt/mul) * mul + 0.5; + } + return curve; +} + +void CLASS foveon_make_curves + (short **curvep, float dq[3], float div[3], float filt) +{ + double mul[3], max=0; + int c; + + FORC3 mul[c] = dq[c]/div[c]; + FORC3 if (max < mul[c]) max = mul[c]; + FORC3 curvep[c] = foveon_make_curve (max, mul[c], filt); +} + +int CLASS foveon_apply_curve (short *curve, int i) +{ + if (abs(i) >= curve[0]) return 0; + return i < 0 ? -curve[1-i] : curve[1+i]; +} + +#define image ((short (*)[4]) image) + +void CLASS foveon_interpolate() +{ + static const short hood[] = { -1,-1, -1,0, -1,1, 0,-1, 0,1, 1,-1, 1,0, 1,1 }; + short *pix, prev[3], *curve[8], (*shrink)[3]; + float cfilt=0, ddft[3][3][2], ppm[3][3][3]; + float cam_xyz[3][3], correct[3][3], last[3][3], trans[3][3]; + float chroma_dq[3], color_dq[3], diag[3][3], div[3]; + float (*black)[3], (*sgain)[3], (*sgrow)[3]; + float fsum[3], val, frow, num; + int row, col, c, i, j, diff, sgx, irow, sum, min, max, limit; + int dim[3], dscr[2][2], dstb[4], (*smrow[7])[3], total[4], ipix[3]; + int work[3][3], smlast, smred, smred_p=0, dev[3]; + int satlev[3], keep[4], active[4]; + unsigned *badpix; + double dsum=0, trsum[3]; + char str[128]; + const char* cp; + + if (verbose) + fprintf (stderr, "Foveon interpolation...\n"); + + foveon_fixed (dscr, 4, "DarkShieldColRange"); + foveon_fixed (ppm[0][0], 27, "PostPolyMatrix"); + foveon_fixed (satlev, 3, "SaturationLevel"); + foveon_fixed (keep, 4, "KeepImageArea"); + foveon_fixed (active, 4, "ActiveImageArea"); + foveon_fixed (chroma_dq, 3, "ChromaDQ"); + foveon_fixed (color_dq, 3, + foveon_camf_param ("IncludeBlocks", "ColorDQ") ? + "ColorDQ" : "ColorDQCamRGB"); + if (foveon_camf_param ("IncludeBlocks", "ColumnFilter")) + foveon_fixed (&cfilt, 1, "ColumnFilter"); + + memset (ddft, 0, sizeof ddft); + if (!foveon_camf_param ("IncludeBlocks", "DarkDrift") + || !foveon_fixed (ddft[1][0], 12, "DarkDrift")) + for (i=0; i < 2; i++) { + foveon_fixed (dstb, 4, i ? "DarkShieldBottom":"DarkShieldTop"); + for (row = dstb[1]; row <= dstb[3]; row++) + for (col = dstb[0]; col <= dstb[2]; col++) + FORC3 ddft[i+1][c][1] += (short) image[row*width+col][c]; + FORC3 ddft[i+1][c][1] /= (dstb[3]-dstb[1]+1) * (dstb[2]-dstb[0]+1); + } + + if (!(cp = foveon_camf_param ("WhiteBalanceIlluminants", model2))) + { fprintf (stderr, "%s: Invalid white balance \"%s\"\n", ifname, model2); + return; } + foveon_fixed (cam_xyz, 9, cp); + foveon_fixed (correct, 9, + foveon_camf_param ("WhiteBalanceCorrections", model2)); + memset (last, 0, sizeof last); + for (i=0; i < 3; i++) + for (j=0; j < 3; j++) + FORC3 last[i][j] += correct[i][c] * cam_xyz[c][j]; + + sprintf (str, "%sRGBNeutral", model2); + if (foveon_camf_param ("IncludeBlocks", str)) + foveon_fixed (div, 3, str); + else { + #define LAST(x,y) last[(i+x)%3][(c+y)%3] + for (i=0; i < 3; i++) + FORC3 diag[c][i] = LAST(1,1)*LAST(2,2) - LAST(1,2)*LAST(2,1); + #undef LAST + FORC3 div[c] = diag[c][0]*0.3127 + diag[c][1]*0.329 + diag[c][2]*0.3583; + } + num = 0; + FORC3 if (num < div[c]) num = div[c]; + FORC3 div[c] /= num; + + memset (trans, 0, sizeof trans); + for (i=0; i < 3; i++) + for (j=0; j < 3; j++) + FORC3 trans[i][j] += rgb_cam[i][c] * last[c][j] * div[j]; + FORC3 trsum[c] = trans[c][0] + trans[c][1] + trans[c][2]; + dsum = (6*trsum[0] + 11*trsum[1] + 3*trsum[2]) / 20; + for (i=0; i < 3; i++) + FORC3 last[i][c] = trans[i][c] * dsum / trsum[i]; + memset (trans, 0, sizeof trans); + for (i=0; i < 3; i++) + for (j=0; j < 3; j++) + FORC3 trans[i][j] += (i==c ? 32 : -1) * last[c][j] / 30; + + foveon_make_curves (curve, color_dq, div, cfilt); + FORC3 chroma_dq[c] /= 3; + foveon_make_curves (curve+3, chroma_dq, div, cfilt); + FORC3 dsum += chroma_dq[c] / div[c]; + curve[6] = foveon_make_curve (dsum, dsum, cfilt); + curve[7] = foveon_make_curve (dsum*2, dsum*2, cfilt); + + sgain = (float (*)[3]) foveon_camf_matrix (dim, "SpatialGain"); + if (!sgain) return; + sgrow = (float (*)[3]) calloc (dim[1], sizeof *sgrow); + sgx = (width + dim[1]-2) / (dim[1]-1); + + black = (float (*)[3]) calloc (height, sizeof *black); + for (row=0; row < height; row++) { + for (i=0; i < 6; i++) + ddft[0][0][i] = ddft[1][0][i] + + row / (height-1.0) * (ddft[2][0][i] - ddft[1][0][i]); + FORC3 black[row][c] = + ( foveon_avg (image[row*width]+c, dscr[0], cfilt) + + foveon_avg (image[row*width]+c, dscr[1], cfilt) * 3 + - ddft[0][c][0] ) / 4 - ddft[0][c][1]; + } + memcpy (black, black+8, sizeof *black*8); + memcpy (black+height-11, black+height-22, 11*sizeof *black); + memcpy (last, black, sizeof last); + + for (row=1; row < height-1; row++) { + FORC3 if (last[1][c] > last[0][c]) { + if (last[1][c] > last[2][c]) + black[row][c] = (last[0][c] > last[2][c]) ? last[0][c]:last[2][c]; + } else + if (last[1][c] < last[2][c]) + black[row][c] = (last[0][c] < last[2][c]) ? last[0][c]:last[2][c]; + memmove (last, last+1, 2*sizeof last[0]); + memcpy (last[2], black[row+1], sizeof last[2]); + } + FORC3 black[row][c] = (last[0][c] + last[1][c])/2; + FORC3 black[0][c] = (black[1][c] + black[3][c])/2; + + val = 1 - exp(-1/24.0); + memcpy (fsum, black, sizeof fsum); + for (row=1; row < height; row++) + FORC3 fsum[c] += black[row][c] = + (black[row][c] - black[row-1][c])*val + black[row-1][c]; + memcpy (last[0], black[height-1], sizeof last[0]); + FORC3 fsum[c] /= height; + for (row = height; row--; ) + FORC3 last[0][c] = black[row][c] = + (black[row][c] - fsum[c] - last[0][c])*val + last[0][c]; + + memset (total, 0, sizeof total); + for (row=2; row < height; row+=4) + for (col=2; col < width; col+=4) { + FORC3 total[c] += (short) image[row*width+col][c]; + total[3]++; + } + for (row=0; row < height; row++) + FORC3 black[row][c] += fsum[c]/2 + total[c]/(total[3]*100.0); + + for (row=0; row < height; row++) { + for (i=0; i < 6; i++) + ddft[0][0][i] = ddft[1][0][i] + + row / (height-1.0) * (ddft[2][0][i] - ddft[1][0][i]); + pix = image[row*width]; + memcpy (prev, pix, sizeof prev); + frow = row / (height-1.0) * (dim[2]-1); + if ((irow = frow) == dim[2]-1) irow--; + frow -= irow; + for (i=0; i < dim[1]; i++) + FORC3 sgrow[i][c] = sgain[ irow *dim[1]+i][c] * (1-frow) + + sgain[(irow+1)*dim[1]+i][c] * frow; + for (col=0; col < width; col++) { + FORC3 { + diff = pix[c] - prev[c]; + prev[c] = pix[c]; + ipix[c] = pix[c] + floor ((diff + (diff*diff >> 14)) * cfilt + - ddft[0][c][1] - ddft[0][c][0] * ((float) col/width - 0.5) + - black[row][c] ); + } + FORC3 { + work[0][c] = ipix[c] * ipix[c] >> 14; + work[2][c] = ipix[c] * work[0][c] >> 14; + work[1][2-c] = ipix[(c+1) % 3] * ipix[(c+2) % 3] >> 14; + } + FORC3 { + for (val=i=0; i < 3; i++) + for ( j=0; j < 3; j++) + val += ppm[c][i][j] * work[i][j]; + ipix[c] = floor ((ipix[c] + floor(val)) * + ( sgrow[col/sgx ][c] * (sgx - col%sgx) + + sgrow[col/sgx+1][c] * (col%sgx) ) / sgx / div[c]); + if (ipix[c] > 32000) ipix[c] = 32000; + pix[c] = ipix[c]; + } + pix += 4; + } + } + free (black); + free (sgrow); + free (sgain); + + if ((badpix = (unsigned int *) foveon_camf_matrix (dim, "BadPixels"))) { + for (i=0; i < dim[0]; i++) { + col = (badpix[i] >> 8 & 0xfff) - keep[0]; + row = (badpix[i] >> 20 ) - keep[1]; + if ((unsigned)(row-1) > height-3 || (unsigned)(col-1) > width-3) + continue; + memset (fsum, 0, sizeof fsum); + for (sum=j=0; j < 8; j++) + if (badpix[i] & (1 << j)) { + FORC3 fsum[c] += (short) + image[(row+hood[j*2])*width+col+hood[j*2+1]][c]; + sum++; + } + if (sum) FORC3 image[row*width+col][c] = fsum[c]/sum; + } + free (badpix); + } + + /* Array for 5x5 Gaussian averaging of red values */ + smrow[6] = (int (*)[3]) calloc (width*5, sizeof **smrow); + merror (smrow[6], "foveon_interpolate()"); + for (i=0; i < 5; i++) + smrow[i] = smrow[6] + i*width; + + /* Sharpen the reds against these Gaussian averages */ + for (smlast=-1, row=2; row < height-2; row++) { + while (smlast < row+2) { + for (i=0; i < 6; i++) + smrow[(i+5) % 6] = smrow[i]; + pix = image[++smlast*width+2]; + for (col=2; col < width-2; col++) { + smrow[4][col][0] = + (pix[0]*6 + (pix[-4]+pix[4])*4 + pix[-8]+pix[8] + 8) >> 4; + pix += 4; + } + } + pix = image[row*width+2]; + for (col=2; col < width-2; col++) { + smred = ( 6 * smrow[2][col][0] + + 4 * (smrow[1][col][0] + smrow[3][col][0]) + + smrow[0][col][0] + smrow[4][col][0] + 8 ) >> 4; + if (col == 2) + smred_p = smred; + i = pix[0] + ((pix[0] - ((smred*7 + smred_p) >> 3)) >> 3); + if (i > 32000) i = 32000; + pix[0] = i; + smred_p = smred; + pix += 4; + } + } + + /* Adjust the brighter pixels for better linearity */ + min = 0xffff; + FORC3 { + i = satlev[c] / div[c]; + if (min > i) min = i; + } + limit = min * 9 >> 4; + for (pix=image[0]; pix < image[height*width]; pix+=4) { + if (pix[0] <= limit || pix[1] <= limit || pix[2] <= limit) + continue; + min = max = pix[0]; + for (c=1; c < 3; c++) { + if (min > pix[c]) min = pix[c]; + if (max < pix[c]) max = pix[c]; + } + if (min >= limit*2) { + pix[0] = pix[1] = pix[2] = max; + } else { + i = 0x4000 - ((min - limit) << 14) / limit; + i = 0x4000 - (i*i >> 14); + i = i*i >> 14; + FORC3 pix[c] += (max - pix[c]) * i >> 14; + } + } +/* + Because photons that miss one detector often hit another, + the sum R+G+B is much less noisy than the individual colors. + So smooth the hues without smoothing the total. + */ + for (smlast=-1, row=2; row < height-2; row++) { + while (smlast < row+2) { + for (i=0; i < 6; i++) + smrow[(i+5) % 6] = smrow[i]; + pix = image[++smlast*width+2]; + for (col=2; col < width-2; col++) { + FORC3 smrow[4][col][c] = (pix[c-4]+2*pix[c]+pix[c+4]+2) >> 2; + pix += 4; + } + } + pix = image[row*width+2]; + for (col=2; col < width-2; col++) { + FORC3 dev[c] = -foveon_apply_curve (curve[7], pix[c] - + ((smrow[1][col][c] + 2*smrow[2][col][c] + smrow[3][col][c]) >> 2)); + sum = (dev[0] + dev[1] + dev[2]) >> 3; + FORC3 pix[c] += dev[c] - sum; + pix += 4; + } + } + for (smlast=-1, row=2; row < height-2; row++) { + while (smlast < row+2) { + for (i=0; i < 6; i++) + smrow[(i+5) % 6] = smrow[i]; + pix = image[++smlast*width+2]; + for (col=2; col < width-2; col++) { + FORC3 smrow[4][col][c] = + (pix[c-8]+pix[c-4]+pix[c]+pix[c+4]+pix[c+8]+2) >> 2; + pix += 4; + } + } + pix = image[row*width+2]; + for (col=2; col < width-2; col++) { + for (total[3]=375, sum=60, c=0; c < 3; c++) { + for (total[c]=i=0; i < 5; i++) + total[c] += smrow[i][col][c]; + total[3] += total[c]; + sum += pix[c]; + } + if (sum < 0) sum = 0; + j = total[3] > 375 ? (sum << 16) / total[3] : sum * 174; + FORC3 pix[c] += foveon_apply_curve (curve[6], + ((j*total[c] + 0x8000) >> 16) - pix[c]); + pix += 4; + } + } + + /* Transform the image to a different colorspace */ + for (pix=image[0]; pix < image[height*width]; pix+=4) { + FORC3 pix[c] -= foveon_apply_curve (curve[c], pix[c]); + sum = (pix[0]+pix[1]+pix[1]+pix[2]) >> 2; + FORC3 pix[c] -= foveon_apply_curve (curve[c], pix[c]-sum); + FORC3 { + for (dsum=i=0; i < 3; i++) + dsum += trans[c][i] * pix[i]; + if (dsum < 0) dsum = 0; + if (dsum > 24000) dsum = 24000; + ipix[c] = dsum + 0.5; + } + FORC3 pix[c] = ipix[c]; + } + + /* Smooth the image bottom-to-top and save at 1/4 scale */ + shrink = (short (*)[3]) calloc ((width/4) * (height/4), sizeof *shrink); + merror (shrink, "foveon_interpolate()"); + for (row = height/4; row--; ) + for (col=0; col < width/4; col++) { + ipix[0] = ipix[1] = ipix[2] = 0; + for (i=0; i < 4; i++) + for (j=0; j < 4; j++) + FORC3 ipix[c] += image[(row*4+i)*width+col*4+j][c]; + FORC3 + if (row+2 > height/4) + shrink[row*(width/4)+col][c] = ipix[c] >> 4; + else + shrink[row*(width/4)+col][c] = + (shrink[(row+1)*(width/4)+col][c]*1840 + ipix[c]*141 + 2048) >> 12; + } + /* From the 1/4-scale image, smooth right-to-left */ + for (row=0; row < (height & ~3); row++) { + ipix[0] = ipix[1] = ipix[2] = 0; + if ((row & 3) == 0) + for (col = width & ~3 ; col--; ) + FORC3 smrow[0][col][c] = ipix[c] = + (shrink[(row/4)*(width/4)+col/4][c]*1485 + ipix[c]*6707 + 4096) >> 13; + + /* Then smooth left-to-right */ + ipix[0] = ipix[1] = ipix[2] = 0; + for (col=0; col < (width & ~3); col++) + FORC3 smrow[1][col][c] = ipix[c] = + (smrow[0][col][c]*1485 + ipix[c]*6707 + 4096) >> 13; + + /* Smooth top-to-bottom */ + if (row == 0) + memcpy (smrow[2], smrow[1], sizeof **smrow * width); + else + for (col=0; col < (width & ~3); col++) + FORC3 smrow[2][col][c] = + (smrow[2][col][c]*6707 + smrow[1][col][c]*1485 + 4096) >> 13; + + /* Adjust the chroma toward the smooth values */ + for (col=0; col < (width & ~3); col++) { + for (i=j=30, c=0; c < 3; c++) { + i += smrow[2][col][c]; + j += image[row*width+col][c]; + } + j = (j << 16) / i; + for (sum=c=0; c < 3; c++) { + ipix[c] = foveon_apply_curve (curve[c+3], + ((smrow[2][col][c] * j + 0x8000) >> 16) - image[row*width+col][c]); + sum += ipix[c]; + } + sum >>= 3; + FORC3 { + i = image[row*width+col][c] + ipix[c] - sum; + if (i < 0) i = 0; + image[row*width+col][c] = i; + } + } + } + free (shrink); + free (smrow[6]); + for (i=0; i < 8; i++) + free (curve[i]); + + /* Trim off the black border */ + active[1] -= keep[1]; + active[3] -= 2; + i = active[2] - active[0]; + for (row=0; row < active[3]-active[1]; row++) + memcpy (image[row*i], image[(row+active[1])*width+active[0]], + i * sizeof *image); + width = i; + height = row; +} +#undef image + +/* END GPL BLOCK */ + +/* + Seach from the current directory up to the root looking for + a ".badpixels" file, and fix those pixels now. + */ +void CLASS bad_pixels() +{ + FILE *fp=NULL; + char *fname, *cp, line[128]; + int len, time, row, col, r, c, rad, tot, n, fixed=0; + + if (!filters) return; + for (len=32 ; ; len *= 2) { + fname = (char *) malloc (len); + if (!fname) return; + if (getcwd (fname, len-16)) break; + free (fname); + if (errno != ERANGE) return; + } +#if defined(WIN32) || defined(DJGPP) + if (fname[1] == ':') + memmove (fname, fname+2, len-2); + for (cp=fname; *cp; cp++) + if (*cp == '\\') *cp = '/'; +#endif + cp = fname + strlen(fname); + if (cp[-1] == '/') cp--; + while (*fname == '/') { + strcpy (cp, "/.badpixels"); + if ((fp = fopen (fname, "r"))) break; + if (cp == fname) break; + while (*--cp != '/'); + } + free (fname); + if (!fp) return; + while (fgets (line, 128, fp)) { + cp = strchr (line, '#'); + if (cp) *cp = 0; + if (sscanf (line, "%d %d %d", &col, &row, &time) != 3) continue; + if ((unsigned) col >= width || (unsigned) row >= height) continue; + if (time > timestamp) continue; + for (tot=n=0, rad=1; rad < 3 && n==0; rad++) + for (r = row-rad; r <= row+rad; r++) + for (c = col-rad; c <= col+rad; c++) + if ((unsigned) r < height && (unsigned) c < width && + (r != row || c != col) && fc(r,c) == fc(row,col)) { + tot += BAYER2(r,c); + n++; + } + BAYER2(row,col) = tot/n; + if (verbose) { + if (!fixed++) + fprintf (stderr, "Fixed bad pixels at:"); + fprintf (stderr, " %d,%d", col, row); + } + } + if (fixed) fputc ('\n', stderr); + fclose (fp); +} + +void CLASS pseudoinverse (double (*in)[3], double (*out)[3], int size) +{ + double work[3][6], num; + int i, j, k; + + for (i=0; i < 3; i++) { + for (j=0; j < 6; j++) + work[i][j] = j == i+3; + for (j=0; j < 3; j++) + for (k=0; k < size; k++) + work[i][j] += in[k][i] * in[k][j]; + } + for (i=0; i < 3; i++) { + num = work[i][i]; + for (j=0; j < 6; j++) + work[i][j] /= num; + for (k=0; k < 3; k++) { + if (k==i) continue; + num = work[k][i]; + for (j=0; j < 6; j++) + work[k][j] -= work[i][j] * num; + } + } + for (i=0; i < size; i++) + for (j=0; j < 3; j++) + for (out[i][j]=k=0; k < 3; k++) + out[i][j] += work[j][k+3] * in[i][k]; +} + +void CLASS cam_xyz_coeff (double cam_xyz[4][3]) +{ + double cam_rgb[4][3], inverse[4][3], num; + int i, j, k; + + for (i=0; i < colors; i++) /* Multiply out XYZ colorspace */ + for (j=0; j < 3; j++) + for (cam_rgb[i][j] = k=0; k < 3; k++) + cam_rgb[i][j] += cam_xyz[i][k] * xyz_rgb[k][j]; + + for (i=0; i < colors; i++) { /* Normalize cam_rgb so that */ + for (num=j=0; j < 3; j++) /* cam_rgb * (1,1,1) is (1,1,1,1) */ + num += cam_rgb[i][j]; + for (j=0; j < 3; j++) + cam_rgb[i][j] /= num; + pre_mul[i] = 1 / num; + } + pseudoinverse (cam_rgb, inverse, colors); + for (raw_color = i=0; i < 3; i++) + for (j=0; j < colors; j++) + rgb_cam[i][j] = inverse[j][i]; +} + +#ifdef COLORCHECK +void CLASS colorcheck() +{ +#define NSQ 24 +// Coordinates of the GretagMacbeth ColorChecker squares +// width, height, 1st_column, 1st_row + static const int cut[NSQ][4] = { + { 241, 231, 234, 274 }, + { 251, 235, 534, 274 }, + { 255, 239, 838, 272 }, + { 255, 240, 1146, 274 }, + { 251, 237, 1452, 278 }, + { 243, 238, 1758, 288 }, + { 253, 253, 218, 558 }, + { 255, 249, 524, 562 }, + { 261, 253, 830, 562 }, + { 260, 255, 1144, 564 }, + { 261, 255, 1450, 566 }, + { 247, 247, 1764, 576 }, + { 255, 251, 212, 862 }, + { 259, 259, 518, 862 }, + { 263, 261, 826, 864 }, + { 265, 263, 1138, 866 }, + { 265, 257, 1450, 872 }, + { 257, 255, 1762, 874 }, + { 257, 253, 212, 1164 }, + { 262, 251, 516, 1172 }, + { 263, 257, 826, 1172 }, + { 263, 255, 1136, 1176 }, + { 255, 252, 1452, 1182 }, + { 257, 253, 1760, 1180 } }; +// ColorChecker Chart under 6500-kelvin illumination + static const double gmb_xyY[NSQ][3] = { + { 0.400, 0.350, 10.1 }, // Dark Skin + { 0.377, 0.345, 35.8 }, // Light Skin + { 0.247, 0.251, 19.3 }, // Blue Sky + { 0.337, 0.422, 13.3 }, // Foliage + { 0.265, 0.240, 24.3 }, // Blue Flower + { 0.261, 0.343, 43.1 }, // Bluish Green + { 0.506, 0.407, 30.1 }, // Orange + { 0.211, 0.175, 12.0 }, // Purplish Blue + { 0.453, 0.306, 19.8 }, // Moderate Red + { 0.285, 0.202, 6.6 }, // Purple + { 0.380, 0.489, 44.3 }, // Yellow Green + { 0.473, 0.438, 43.1 }, // Orange Yellow + { 0.187, 0.129, 6.1 }, // Blue + { 0.305, 0.478, 23.4 }, // Green + { 0.539, 0.313, 12.0 }, // Red + { 0.448, 0.470, 59.1 }, // Yellow + { 0.364, 0.233, 19.8 }, // Magenta + { 0.196, 0.252, 19.8 }, // Cyan + { 0.310, 0.316, 90.0 }, // White + { 0.310, 0.316, 59.1 }, // Neutral 8 + { 0.310, 0.316, 36.2 }, // Neutral 6.5 + { 0.310, 0.316, 19.8 }, // Neutral 5 + { 0.310, 0.316, 9.0 }, // Neutral 3.5 + { 0.310, 0.316, 3.1 } }; // Black + double gmb_cam[NSQ][4], gmb_xyz[NSQ][3]; + double inverse[NSQ][3], cam_xyz[4][3], num; + int c, i, j, k, sq, row, col, count[4]; + + memset (gmb_cam, 0, sizeof gmb_cam); + for (sq=0; sq < NSQ; sq++) { + FORCC count[c] = 0; + for (row=cut[sq][3]; row < cut[sq][3]+cut[sq][1]; row++) + for (col=cut[sq][2]; col < cut[sq][2]+cut[sq][0]; col++) { + c = FC(row,col); + if (c >= colors) c -= 2; + gmb_cam[sq][c] += BAYER(row,col); + count[c]++; + } + FORCC gmb_cam[sq][c] = gmb_cam[sq][c]/count[c] - black; + gmb_xyz[sq][0] = gmb_xyY[sq][2] * gmb_xyY[sq][0] / gmb_xyY[sq][1]; + gmb_xyz[sq][1] = gmb_xyY[sq][2]; + gmb_xyz[sq][2] = gmb_xyY[sq][2] * + (1 - gmb_xyY[sq][0] - gmb_xyY[sq][1]) / gmb_xyY[sq][1]; + } + pseudoinverse (gmb_xyz, inverse, NSQ); + for (i=0; i < colors; i++) + for (j=0; j < 3; j++) + for (cam_xyz[i][j] = k=0; k < NSQ; k++) + cam_xyz[i][j] += gmb_cam[k][i] * inverse[k][j]; + cam_xyz_coeff (cam_xyz); + if (verbose) { + printf (" { \"%s %s\", %d,\n\t{", make, model, black); + num = 10000 / (cam_xyz[1][0] + cam_xyz[1][1] + cam_xyz[1][2]); + FORCC for (j=0; j < 3; j++) + printf ("%c%d", (c | j) ? ',':' ', (int) (cam_xyz[c][j] * num + 0.5)); + puts (" } },"); + } +#undef NSQ +} +#endif + +void CLASS scale_colors() +{ + int row, col, x, y, c, val; + int min[4], max[4], sum[8]; + double dsum[8], dmin, dmax; + float scale_mul[4]; + + maximum -= black; + if (use_auto_wb || (use_camera_wb && cam_mul[0] == -1)) { + FORC4 min[c] = INT_MAX; + FORC4 max[c] = 0; + memset (dsum, 0, sizeof dsum); + for (row=0; row < height-7; row += 8) + for (col=0; col < width-7; col += 8) { + memset (sum, 0, sizeof sum); + for (y=row; y < row+8; y++) + for (x=col; x < col+8; x++) + FORC4 { + val = image[y*width+x][c]; + if (!val) continue; + if (min[c] > val) min[c] = val; + if (max[c] < val) max[c] = val; + val -= black; + if (val > maximum-25) goto skip_block; + if (val < 0) val = 0; + sum[c] += val; + sum[c+4]++; + } + for (c=0; c < 8; c++) dsum[c] += sum[c]; +skip_block: + continue; + } + FORC4 if (dsum[c]) pre_mul[c] = dsum[c+4] / dsum[c]; + } + if (use_camera_wb && cam_mul[0] != -1) { + memset (sum, 0, sizeof sum); + for (row=0; row < 8; row++) + for (col=0; col < 8; col++) { + c = FC(row,col); + if ((val = white[row][col] - black) > 0) + sum[c] += val; + sum[c+4]++; + } + if (sum[0] && sum[1] && sum[2] && sum[3]) + FORC4 pre_mul[c] = (float) sum[c+4] / sum[c]; + else if (cam_mul[0] && cam_mul[2]) + memcpy (pre_mul, cam_mul, sizeof pre_mul); + else + fprintf (stderr, "%s: Cannot use camera white balance.\n", ifname); + } + if (user_mul[0]) + memcpy (pre_mul, user_mul, sizeof pre_mul); + if (pre_mul[3] == 0) pre_mul[3] = colors < 4 ? pre_mul[1] : 1; + for (dmin=DBL_MAX, dmax=c=0; c < 4; c++) { + if (dmin > pre_mul[c]) + dmin = pre_mul[c]; + if (dmax < pre_mul[c]) + dmax = pre_mul[c]; + } + if (!highlight) dmax = dmin; + FORC4 scale_mul[c] = (pre_mul[c] /= dmax) * 65535.0 / maximum; + if (verbose) { + fprintf (stderr, "Scaling with black=%d, pre_mul[] =", black); + FORC4 fprintf (stderr, " %f", pre_mul[c]); + fputc ('\n', stderr); + } + for (row=0; row < height; row++) + for (col=0; col < width; col++) + FORC4 { + val = image[row*width+col][c]; + if (!val) continue; + val -= black; + val *= scale_mul[c]; + image[row*width+col][c] = CLIP(val); + } + if (filters && colors == 3) { + if (four_color_rgb) { + colors++; + FORC3 rgb_cam[c][3] = rgb_cam[c][1] /= 2; + } else { + for (row = FC(1,0) >> 1; row < height; row+=2) + for (col = FC(row,1) & 1; col < width; col+=2) + image[row*width+col][1] = image[row*width+col][3]; + filters &= ~((filters & 0x55555555) << 1); + } + } +} + +void CLASS border_interpolate (int border) +{ + unsigned row, col, y, x, f, c, sum[8]; + + for (row=0; row < height; row++) + for (col=0; col < width; col++) { + if (col==border && row >= border && row < height-border) + col = width-border; + memset (sum, 0, sizeof sum); + for (y=row-1; y != row+2; y++) + for (x=col-1; x != col+2; x++) + if (y < height && x < width) { + f = fc(y,x); + sum[f] += image[y*width+x][f]; + sum[f+4]++; + } + f = fc(row,col); + FORCC if (c != f && sum[c+4]) + image[row*width+col][c] = sum[c] / sum[c+4]; + } +} + +void CLASS lin_interpolate() +{ + int code[16][16][32], *ip, sum[4]; + int c, i, x, y, row, col, shift, color; + ushort *pix; + + if (verbose) fprintf (stderr, "Bilinear interpolation...\n"); + + border_interpolate(1); + for (row=0; row < 16; row++) + for (col=0; col < 16; col++) { + ip = code[row][col]; + memset (sum, 0, sizeof sum); + for (y=-1; y <= 1; y++) + for (x=-1; x <= 1; x++) { + shift = (y==0) + (x==0); + if (shift == 2) continue; + color = fc(row+y,col+x); + *ip++ = (width*y + x)*4 + color; + *ip++ = shift; + *ip++ = color; + sum[color] += 1 << shift; + } + FORCC + if (c != fc(row,col)) { + *ip++ = c; + *ip++ = sum[c]; + } + } + for (row=1; row < height-1; row++) + for (col=1; col < width-1; col++) { + pix = image[row*width+col]; + ip = code[row & 15][col & 15]; + memset (sum, 0, sizeof sum); + for (i=8; i--; ip+=3) + sum[ip[2]] += pix[ip[0]] << ip[1]; + for (i=colors; --i; ip+=2) + pix[ip[0]] = sum[ip[0]] / ip[1]; + } +} + +/* + This algorithm is officially called: + + "Interpolation using a Threshold-based variable number of gradients" + + described in http://scien.stanford.edu/class/psych221/projects/99/tingchen/algodep/vargra.html + + I've extended the basic idea to work with non-Bayer filter arrays. + Gradients are numbered clockwise from NW=0 to W=7. + */ +void CLASS vng_interpolate() +{ + static const signed char *cp, terms[] = { + -2,-2,+0,-1,0,0x01, -2,-2,+0,+0,1,0x01, -2,-1,-1,+0,0,0x01, + -2,-1,+0,-1,0,0x02, -2,-1,+0,+0,0,0x03, -2,-1,+0,+1,1,0x01, + -2,+0,+0,-1,0,0x06, -2,+0,+0,+0,1,0x02, -2,+0,+0,+1,0,0x03, + -2,+1,-1,+0,0,0x04, -2,+1,+0,-1,1,0x04, -2,+1,+0,+0,0,0x06, + -2,+1,+0,+1,0,0x02, -2,+2,+0,+0,1,0x04, -2,+2,+0,+1,0,0x04, + -1,-2,-1,+0,0,0x80, -1,-2,+0,-1,0,0x01, -1,-2,+1,-1,0,0x01, + -1,-2,+1,+0,1,0x01, -1,-1,-1,+1,0,0x88, -1,-1,+1,-2,0,0x40, + -1,-1,+1,-1,0,0x22, -1,-1,+1,+0,0,0x33, -1,-1,+1,+1,1,0x11, + -1,+0,-1,+2,0,0x08, -1,+0,+0,-1,0,0x44, -1,+0,+0,+1,0,0x11, + -1,+0,+1,-2,1,0x40, -1,+0,+1,-1,0,0x66, -1,+0,+1,+0,1,0x22, + -1,+0,+1,+1,0,0x33, -1,+0,+1,+2,1,0x10, -1,+1,+1,-1,1,0x44, + -1,+1,+1,+0,0,0x66, -1,+1,+1,+1,0,0x22, -1,+1,+1,+2,0,0x10, + -1,+2,+0,+1,0,0x04, -1,+2,+1,+0,1,0x04, -1,+2,+1,+1,0,0x04, + +0,-2,+0,+0,1,0x80, +0,-1,+0,+1,1,0x88, +0,-1,+1,-2,0,0x40, + +0,-1,+1,+0,0,0x11, +0,-1,+2,-2,0,0x40, +0,-1,+2,-1,0,0x20, + +0,-1,+2,+0,0,0x30, +0,-1,+2,+1,1,0x10, +0,+0,+0,+2,1,0x08, + +0,+0,+2,-2,1,0x40, +0,+0,+2,-1,0,0x60, +0,+0,+2,+0,1,0x20, + +0,+0,+2,+1,0,0x30, +0,+0,+2,+2,1,0x10, +0,+1,+1,+0,0,0x44, + +0,+1,+1,+2,0,0x10, +0,+1,+2,-1,1,0x40, +0,+1,+2,+0,0,0x60, + +0,+1,+2,+1,0,0x20, +0,+1,+2,+2,0,0x10, +1,-2,+1,+0,0,0x80, + +1,-1,+1,+1,0,0x88, +1,+0,+1,+2,0,0x08, +1,+0,+2,-1,0,0x40, + +1,+0,+2,+1,0,0x10 + }, chood[] = { -1,-1, -1,0, -1,+1, 0,+1, +1,+1, +1,0, +1,-1, 0,-1 }; + ushort (*brow[5])[4], *pix; + int prow=7, pcol=1, *ip, *code[16][16], gval[8], gmin, gmax, sum[4]; + int row, col, x, y, x1, x2, y1, y2, t, weight, grads, color, diag; + int g, diff, thold, num, c; + + lin_interpolate(); + if (verbose) fprintf (stderr, "VNG interpolation...\n"); + + if (filters == 1) prow = pcol = 15; + ip = (int *) calloc ((prow+1)*(pcol+1), 1280); + merror (ip, "vng_interpolate()"); + for (row=0; row <= prow; row++) /* Precalculate for VNG */ + for (col=0; col <= pcol; col++) { + code[row][col] = ip; + for (cp=terms, t=0; t < 64; t++) { + y1 = *cp++; x1 = *cp++; + y2 = *cp++; x2 = *cp++; + weight = *cp++; + grads = *cp++; + color = fc(row+y1,col+x1); + if (fc(row+y2,col+x2) != color) continue; + diag = (fc(row,col+1) == color && fc(row+1,col) == color) ? 2:1; + if (abs(y1-y2) == diag && abs(x1-x2) == diag) continue; + *ip++ = (y1*width + x1)*4 + color; + *ip++ = (y2*width + x2)*4 + color; + *ip++ = weight; + for (g=0; g < 8; g++) + if (grads & 1< gval[g]) gmin = gval[g]; + if (gmax < gval[g]) gmax = gval[g]; + } + if (gmax == 0) { + memcpy (brow[2][col], pix, sizeof *image); + continue; + } + thold = gmin + (gmax >> 1); + memset (sum, 0, sizeof sum); + color = fc(row,col); + for (num=g=0; g < 8; g++,ip+=2) { /* Average the neighbors */ + if (gval[g] <= thold) { + FORCC + if (c == color && ip[1]) + sum[c] += (pix[c] + pix[ip[1]]) >> 1; + else + sum[c] += pix[ip[0] + c]; + num++; + } + } + FORCC { /* Save to buffer */ + t = pix[color]; + if (c != color) + t += (sum[c] - sum[color]) / num; + brow[2][col][c] = CLIP(t); + } + } + if (row > 3) /* Write buffer to image */ + memcpy (image[(row-2)*width+2], brow[0]+2, (width-4)*sizeof *image); + for (g=0; g < 4; g++) + brow[(g-1) & 3] = brow[g]; + } + memcpy (image[(row-2)*width+2], brow[0]+2, (width-4)*sizeof *image); + memcpy (image[(row-1)*width+2], brow[1]+2, (width-4)*sizeof *image); + free (brow[4]); + free (code[0][0]); +} + +void CLASS cam_to_cielab (ushort cam[4], float lab[3]) +{ + int c, i, j, k; + float r, xyz[3]; + static float cbrt[0x10000], xyz_cam[3][4]; + + if (cam == NULL) { + for (i=0; i < 0x10000; i++) { + r = i / 65535.0; + cbrt[i] = r > 0.008856 ? pow(r,1/3.0) : 7.787*r + 16/116.0; + } + for (i=0; i < 3; i++) + for (j=0; j < colors; j++) + for (xyz_cam[i][j] = k=0; k < 3; k++) + xyz_cam[i][j] += xyz_rgb[i][k] * rgb_cam[k][j] / d65_white[i]; + } else { + xyz[0] = xyz[1] = xyz[2] = 0.5; + FORCC { + xyz[0] += xyz_cam[0][c] * cam[c]; + xyz[1] += xyz_cam[1][c] * cam[c]; + xyz[2] += xyz_cam[2][c] * cam[c]; + } + xyz[0] = cbrt[CLIP((int) xyz[0])]; + xyz[1] = cbrt[CLIP((int) xyz[1])]; + xyz[2] = cbrt[CLIP((int) xyz[2])]; + lab[0] = 116 * xyz[1] - 16; + lab[1] = 500 * (xyz[0] - xyz[1]); + lab[2] = 200 * (xyz[1] - xyz[2]); + } +} + +/* + Adaptive Homogeneity-Directed interpolation is based on + the work of Keigo Hirakawa, Thomas Parks, and Paul Lee. + */ +#define TS 256 /* Tile Size */ + +void CLASS ahd_interpolate() +{ + int i, j, top, left, row, col, tr, tc, fc, c, d, val, hm[2]; + ushort (*pix)[4], (*rix)[3]; + static const int dir[4] = { -1, 1, -TS, TS }; + unsigned ldiff[2][4], abdiff[2][4], leps, abeps; + float flab[3]; + ushort (*rgb)[TS][TS][3]; + short (*lab)[TS][TS][3]; + char (*homo)[TS][TS], *buffer; + + if (verbose) fprintf (stderr, "AHD interpolation...\n"); + + border_interpolate(3); + buffer = (char *) malloc (26*TS*TS); /* 1664 kB */ + merror (buffer, "ahd_interpolate()"); + rgb = (ushort(*)[TS][TS][3]) buffer; + lab = (short (*)[TS][TS][3])(buffer + 12*TS*TS); + homo = (char (*)[TS][TS]) (buffer + 24*TS*TS); + + for (top=0; top < height; top += TS-6) + for (left=0; left < width; left += TS-6) { + memset (rgb, 0, 12*TS*TS); + +/* Interpolate green horizontally and vertically: */ + for (row = top < 2 ? 2:top; row < top+TS && row < height-2; row++) { + col = left + (FC(row,left) == 1); + if (col < 2) col += 2; + for (fc = FC(row,col); col < left+TS && col < width-2; col+=2) { + pix = image + row*width+col; + val = ((pix[-1][1] + pix[0][fc] + pix[1][1]) * 2 + - pix[-2][fc] - pix[2][fc]) >> 2; + rgb[0][row-top][col-left][1] = ULIM(val,pix[-1][1],pix[1][1]); + val = ((pix[-width][1] + pix[0][fc] + pix[width][1]) * 2 + - pix[-2*width][fc] - pix[2*width][fc]) >> 2; + rgb[1][row-top][col-left][1] = ULIM(val,pix[-width][1],pix[width][1]); + } + } +/* Interpolate red and blue, and convert to CIELab: */ + for (d=0; d < 2; d++) + for (row=top+1; row < top+TS-1 && row < height-1; row++) + for (col=left+1; col < left+TS-1 && col < width-1; col++) { + pix = image + row*width+col; + rix = &rgb[d][row-top][col-left]; + if ((c = 2 - FC(row,col)) == 1) { + c = FC(row+1,col); + val = pix[0][1] + (( pix[-1][2-c] + pix[1][2-c] + - rix[-1][1] - rix[1][1] ) >> 1); + rix[0][2-c] = CLIP(val); + val = pix[0][1] + (( pix[-width][c] + pix[width][c] + - rix[-TS][1] - rix[TS][1] ) >> 1); + } else + val = rix[0][1] + (( pix[-width-1][c] + pix[-width+1][c] + + pix[+width-1][c] + pix[+width+1][c] + - rix[-TS-1][1] - rix[-TS+1][1] + - rix[+TS-1][1] - rix[+TS+1][1] + 1) >> 2); + rix[0][c] = CLIP(val); + c = FC(row,col); + rix[0][c] = pix[0][c]; + cam_to_cielab (rix[0], flab); + FORC3 lab[d][row-top][col-left][c] = 64*flab[c]; + } +/* Build homogeneity maps from the CIELab images: */ + memset (homo, 0, 2*TS*TS); + for (row=top+2; row < top+TS-2 && row < height; row++) { + tr = row-top; + for (col=left+2; col < left+TS-2 && col < width; col++) { + tc = col-left; + for (d=0; d < 2; d++) + for (i=0; i < 4; i++) + ldiff[d][i] = ABS(lab[d][tr][tc][0]-lab[d][tr][tc+dir[i]][0]); + leps = MIN(MAX(ldiff[0][0],ldiff[0][1]), + MAX(ldiff[1][2],ldiff[1][3])); + for (d=0; d < 2; d++) + for (i=0; i < 4; i++) + if (i >> 1 == d || ldiff[d][i] <= leps) + abdiff[d][i] = SQR(lab[d][tr][tc][1]-lab[d][tr][tc+dir[i]][1]) + + SQR(lab[d][tr][tc][2]-lab[d][tr][tc+dir[i]][2]); + abeps = MIN(MAX(abdiff[0][0],abdiff[0][1]), + MAX(abdiff[1][2],abdiff[1][3])); + for (d=0; d < 2; d++) + for (i=0; i < 4; i++) + if (ldiff[d][i] <= leps && abdiff[d][i] <= abeps) + homo[d][tr][tc]++; + } + } +/* Combine the most homogenous pixels for the final result: */ + for (row=top+3; row < top+TS-3 && row < height-3; row++) { + tr = row-top; + for (col=left+3; col < left+TS-3 && col < width-3; col++) { + tc = col-left; + for (d=0; d < 2; d++) + for (hm[d]=0, i=tr-1; i <= tr+1; i++) + for (j=tc-1; j <= tc+1; j++) + hm[d] += homo[d][i][j]; + if (hm[0] != hm[1]) + FORC3 image[row*width+col][c] = rgb[hm[1] > hm[0]][tr][tc][c]; + else + FORC3 image[row*width+col][c] = + (rgb[0][tr][tc][c] + rgb[1][tr][tc][c]) >> 1; + } + } + } + free (buffer); +} +#undef TS + +/* + Bilateral Filtering was developed by C. Tomasi and R. Manduchi. + */ +void CLASS bilateral_filter() +{ + float (**window)[7], *kernel, scale_r, elut[1024], sum[5]; + int c, i, wr, ws, wlast, row, col, y, x; + unsigned sep; + + if (verbose) fprintf (stderr, "Bilateral filtering...\n"); + + wr = ceil(sigma_d*2); /* window radius */ + ws = 2*wr + 1; /* window size */ + window = (float (**)[7]) calloc ((ws+1)*sizeof *window + + ws*width*sizeof **window + ws*sizeof *kernel, 1); + merror (window, "bilateral_filter()"); + for (i=0; i <= ws; i++) + window[i] = (float (*)[7]) (window+ws+1) + i*width; + kernel = (float *) window[ws] + wr; + for (i=-wr; i <= wr; i++) + kernel[i] = 256 / (2*SQR(sigma_d)) * i*i + 0.25; + scale_r = 256 / (2*SQR(sigma_r)); + for (i=0; i < 1024; i++) + elut[i] = exp (-i/256.0); + + for (wlast=-1, row=0; row < height; row++) { + while (wlast < row+wr) { + wlast++; + for (i=0; i <= ws; i++) /* rotate window rows */ + window[(ws+i) % (ws+1)] = window[i]; + if (wlast < height) + for (col=0; col < width; col++) { + FORCC window[ws-1][col][c] = image[wlast*width+col][c]; + cam_to_cielab (image[wlast*width+col], window[ws-1][col]+4); + } + } + for (col=0; col < width; col++) { + memset (sum, 0, sizeof sum); + for (y=-wr; y <= wr; y++) + if ((unsigned)(row+y) < height) + for (x=-wr; x <= wr; x++) + if ((unsigned)(col+x) < width) { + sep = ( SQR(window[wr+y][col+x][4] - window[wr][col][4]) + + SQR(window[wr+y][col+x][5] - window[wr][col][5]) + + SQR(window[wr+y][col+x][6] - window[wr][col][6]) ) + * scale_r + kernel[y] + kernel[x]; + if (sep < 1024) { + FORCC sum[c] += elut[sep] * window[wr+y][col+x][c]; + sum[4] += elut[sep]; + } + } + FORCC image[row*width+col][c] = sum[c]/sum[4]; + } + } + free (window); +} + +#define SCALE (4 >> shrink) +void CLASS recover_highlights() +{ + float *map, sum, wgt, grow; + int hsat[4], count, spread, change, val, i; + unsigned high, wide, mrow, mcol, row, col, kc, c, d, y, x; + ushort *pixel; + static const signed char dir[8][2] = + { {-1,-1}, {-1,0}, {-1,1}, {0,1}, {1,1}, {1,0}, {1,-1}, {0,-1} }; + + if (verbose) fprintf (stderr, "Highlight recovery...\n"); + + grow = pow (2, 4-highlight); + FORCC hsat[c] = 32000 * pre_mul[c]; + for (kc=0, c=1; c < colors; c++) + if (pre_mul[kc] < pre_mul[c]) kc = c; + high = height / SCALE; + wide = width / SCALE; + map = (float *) calloc (high*wide, sizeof *map); + merror (map, "recover_highlights()"); + FORCC if (c != kc) { + memset (map, 0, high*wide*sizeof *map); + for (mrow=0; mrow < high; mrow++) + for (mcol=0; mcol < wide; mcol++) { + sum = wgt = count = 0; + for (row = mrow*SCALE; row < (mrow+1)*SCALE; row++) + for (col = mcol*SCALE; col < (mcol+1)*SCALE; col++) { + pixel = image[row*width+col]; + if (pixel[c] / hsat[c] == 1 && pixel[kc] > 24000) { + sum += pixel[c]; + wgt += pixel[kc]; + count++; + } + } + if (count == SCALE*SCALE) + map[mrow*wide+mcol] = sum / wgt; + } + for (spread = 32/grow; spread--; ) { + for (mrow=0; mrow < high; mrow++) + for (mcol=0; mcol < wide; mcol++) { + if (map[mrow*wide+mcol]) continue; + sum = count = 0; + for (d=0; d < 8; d++) { + y = mrow + dir[d][0]; + x = mcol + dir[d][1]; + if (y < high && x < wide && map[y*wide+x] > 0) { + sum += (1 + (d & 1)) * map[y*wide+x]; + count += 1 + (d & 1); + } + } + if (count > 3) + map[mrow*wide+mcol] = - (sum+grow) / (count+grow); + } + for (change=i=0; i < high*wide; i++) + if (map[i] < 0) { + map[i] = -map[i]; + change = 1; + } + if (!change) break; + } + for (i=0; i < high*wide; i++) + if (map[i] == 0) map[i] = 1; + for (mrow=0; mrow < high; mrow++) + for (mcol=0; mcol < wide; mcol++) { + for (row = mrow*SCALE; row < (mrow+1)*SCALE; row++) + for (col = mcol*SCALE; col < (mcol+1)*SCALE; col++) { + pixel = image[row*width+col]; + if (pixel[c] / hsat[c] > 1) { + val = pixel[kc] * map[mrow*wide+mcol]; + if (pixel[c] < val) pixel[c] = CLIP(val); + } + } + } + } + free (map); +} +#undef SCALE + +void CLASS tiff_get (unsigned base, + unsigned *tag, unsigned *type, unsigned *len, unsigned *save) +{ + *tag = get2(); + *type = get2(); + *len = get4(); + *save = ftell(ifp) + 4; + if (*len * ("1112481124848"[*type < 13 ? *type:0]-'0') > 4) + fseek (ifp, get4()+base, SEEK_SET); +} + +void CLASS parse_thumb_note (int base, unsigned toff, unsigned tlen) +{ + unsigned entries, tag, type, len, save; + + entries = get2(); + while (entries--) { + tiff_get (base, &tag, &type, &len, &save); + if (tag == toff) thumb_offset = get4(); + if (tag == tlen) thumb_length = get4(); + fseek (ifp, save, SEEK_SET); + } +} + +void CLASS parse_makernote (int base) +{ + static const uchar xlat[2][256] = { + { 0xc1,0xbf,0x6d,0x0d,0x59,0xc5,0x13,0x9d,0x83,0x61,0x6b,0x4f,0xc7,0x7f,0x3d,0x3d, + 0x53,0x59,0xe3,0xc7,0xe9,0x2f,0x95,0xa7,0x95,0x1f,0xdf,0x7f,0x2b,0x29,0xc7,0x0d, + 0xdf,0x07,0xef,0x71,0x89,0x3d,0x13,0x3d,0x3b,0x13,0xfb,0x0d,0x89,0xc1,0x65,0x1f, + 0xb3,0x0d,0x6b,0x29,0xe3,0xfb,0xef,0xa3,0x6b,0x47,0x7f,0x95,0x35,0xa7,0x47,0x4f, + 0xc7,0xf1,0x59,0x95,0x35,0x11,0x29,0x61,0xf1,0x3d,0xb3,0x2b,0x0d,0x43,0x89,0xc1, + 0x9d,0x9d,0x89,0x65,0xf1,0xe9,0xdf,0xbf,0x3d,0x7f,0x53,0x97,0xe5,0xe9,0x95,0x17, + 0x1d,0x3d,0x8b,0xfb,0xc7,0xe3,0x67,0xa7,0x07,0xf1,0x71,0xa7,0x53,0xb5,0x29,0x89, + 0xe5,0x2b,0xa7,0x17,0x29,0xe9,0x4f,0xc5,0x65,0x6d,0x6b,0xef,0x0d,0x89,0x49,0x2f, + 0xb3,0x43,0x53,0x65,0x1d,0x49,0xa3,0x13,0x89,0x59,0xef,0x6b,0xef,0x65,0x1d,0x0b, + 0x59,0x13,0xe3,0x4f,0x9d,0xb3,0x29,0x43,0x2b,0x07,0x1d,0x95,0x59,0x59,0x47,0xfb, + 0xe5,0xe9,0x61,0x47,0x2f,0x35,0x7f,0x17,0x7f,0xef,0x7f,0x95,0x95,0x71,0xd3,0xa3, + 0x0b,0x71,0xa3,0xad,0x0b,0x3b,0xb5,0xfb,0xa3,0xbf,0x4f,0x83,0x1d,0xad,0xe9,0x2f, + 0x71,0x65,0xa3,0xe5,0x07,0x35,0x3d,0x0d,0xb5,0xe9,0xe5,0x47,0x3b,0x9d,0xef,0x35, + 0xa3,0xbf,0xb3,0xdf,0x53,0xd3,0x97,0x53,0x49,0x71,0x07,0x35,0x61,0x71,0x2f,0x43, + 0x2f,0x11,0xdf,0x17,0x97,0xfb,0x95,0x3b,0x7f,0x6b,0xd3,0x25,0xbf,0xad,0xc7,0xc5, + 0xc5,0xb5,0x8b,0xef,0x2f,0xd3,0x07,0x6b,0x25,0x49,0x95,0x25,0x49,0x6d,0x71,0xc7 }, + { 0xa7,0xbc,0xc9,0xad,0x91,0xdf,0x85,0xe5,0xd4,0x78,0xd5,0x17,0x46,0x7c,0x29,0x4c, + 0x4d,0x03,0xe9,0x25,0x68,0x11,0x86,0xb3,0xbd,0xf7,0x6f,0x61,0x22,0xa2,0x26,0x34, + 0x2a,0xbe,0x1e,0x46,0x14,0x68,0x9d,0x44,0x18,0xc2,0x40,0xf4,0x7e,0x5f,0x1b,0xad, + 0x0b,0x94,0xb6,0x67,0xb4,0x0b,0xe1,0xea,0x95,0x9c,0x66,0xdc,0xe7,0x5d,0x6c,0x05, + 0xda,0xd5,0xdf,0x7a,0xef,0xf6,0xdb,0x1f,0x82,0x4c,0xc0,0x68,0x47,0xa1,0xbd,0xee, + 0x39,0x50,0x56,0x4a,0xdd,0xdf,0xa5,0xf8,0xc6,0xda,0xca,0x90,0xca,0x01,0x42,0x9d, + 0x8b,0x0c,0x73,0x43,0x75,0x05,0x94,0xde,0x24,0xb3,0x80,0x34,0xe5,0x2c,0xdc,0x9b, + 0x3f,0xca,0x33,0x45,0xd0,0xdb,0x5f,0xf5,0x52,0xc3,0x21,0xda,0xe2,0x22,0x72,0x6b, + 0x3e,0xd0,0x5b,0xa8,0x87,0x8c,0x06,0x5d,0x0f,0xdd,0x09,0x19,0x93,0xd0,0xb9,0xfc, + 0x8b,0x0f,0x84,0x60,0x33,0x1c,0x9b,0x45,0xf1,0xf0,0xa3,0x94,0x3a,0x12,0x77,0x33, + 0x4d,0x44,0x78,0x28,0x3c,0x9e,0xfd,0x65,0x57,0x16,0x94,0x6b,0xfb,0x59,0xd0,0xc8, + 0x22,0x36,0xdb,0xd2,0x63,0x98,0x43,0xa1,0x04,0x87,0x86,0xf7,0xa6,0x26,0xbb,0xd6, + 0x59,0x4d,0xbf,0x6a,0x2e,0xaa,0x2b,0xef,0xe6,0x78,0xb6,0x4e,0xe0,0x2f,0xdc,0x7c, + 0xbe,0x57,0x19,0x32,0x7e,0x2a,0xd0,0xb8,0xba,0x29,0x00,0x3c,0x52,0x7d,0xa8,0x49, + 0x3b,0x2d,0xeb,0x25,0x49,0xfa,0xa3,0xaa,0x39,0xa7,0xc5,0xa7,0x50,0x11,0x36,0xfb, + 0xc6,0x67,0x4a,0xf5,0xa5,0x12,0x65,0x7e,0xb0,0xdf,0xaf,0x4e,0xb3,0x61,0x7f,0x2f } }; + unsigned offset=0, entries, tag, type, len, save, c; + unsigned ver97=0, serial=0, i, wb[4]={0,0,0,0}; + uchar buf97[324], ci, cj, ck; + short sorder; + char buf[10]; +/* + The MakerNote might have its own TIFF header (possibly with + its own byte-order!), or it might just be a table. + */ + sorder = order; + fread (buf, 1, 10, ifp); + if (!strncmp (buf,"KDK" ,3) || /* these aren't TIFF tables */ + !strncmp (buf,"VER" ,3) || + !strncmp (buf,"IIII",4) || + !strncmp (buf,"MMMM",4)) return; + if (!strncmp (buf,"KC" ,2) || /* Konica KD-400Z, KD-510Z */ + !strncmp (buf,"MLY" ,3)) { /* Minolta DiMAGE G series */ + order = 0x4d4d; + while ((i=ftell(ifp)) < data_offset && i < 16384) { + wb[0] = wb[2]; wb[2] = wb[1]; wb[1] = wb[3]; + wb[3] = get2(); + if (wb[1] == 256 && wb[3] == 256 && + wb[0] > 256 && wb[0] < 640 && wb[2] > 256 && wb[2] < 640) + FORC4 cam_mul[c] = wb[c]; + } + goto quit; + } + if (!strcmp (buf,"Nikon")) { + base = ftell(ifp); + order = get2(); + if (get2() != 42) goto quit; + offset = get4(); + fseek (ifp, offset-8, SEEK_CUR); + } else if (!strncmp (buf,"FUJIFILM",8) || + !strncmp (buf,"SONY",4) || + !strcmp (buf,"Panasonic")) { + order = 0x4949; + fseek (ifp, 2, SEEK_CUR); + } else if (!strcmp (buf,"OLYMP") || + !strcmp (buf,"LEICA") || + !strcmp (buf,"Ricoh") || + !strcmp (buf,"EPSON")) + fseek (ifp, -2, SEEK_CUR); + else if (!strcmp (buf,"AOC") || + !strcmp (buf,"QVC")) + fseek (ifp, -4, SEEK_CUR); + else fseek (ifp, -10, SEEK_CUR); + + entries = get2(); + if (entries > 1000) return; + while (entries--) { + tiff_get (base, &tag, &type, &len, &save); + if (tag == 2 && strstr(make,"NIKON")) + iso_speed = (get2(),get2()); + if (tag == 4 && len == 27) { + iso_speed = 50 * pow (2, (get4(),get2())/32.0 - 4); + aperture = (get2(), pow (2, get2()/64.0)); + shutter = pow (2, ((short) get2())/-32.0); + } + if (tag == 8 && type == 4) + shot_order = get4(); + if (tag == 0xc && len == 4) { + cam_mul[0] = getrat(); + cam_mul[2] = getrat(); + } + if (tag == 0x10 && type == 4) + unique_id = get4(); + if (tag == 0x14 && len == 2560 && type == 7) { + fseek (ifp, 1248, SEEK_CUR); + goto get2_256; + } + if (strstr(make,"PENTAX")) { + if (tag == 0x1b) tag = 0x1018; + if (tag == 0x1c) tag = 0x1017; + } + if (tag == 0x1d) + while ((c = fgetc(ifp))) + serial = serial*10 + (isdigit(c) ? c - '0' : c % 10); + if (tag == 0x81 && type == 4) { + data_offset = get4(); + fseek (ifp, data_offset + 41, SEEK_SET); + raw_height = get2() * 2; + raw_width = get2(); + filters = 0x61616161; + } + if ((tag == 0x81 && type == 7) || + (tag == 0x100 && type == 7) || + (tag == 0x280 && type == 1)) { + thumb_offset = ftell(ifp); + thumb_length = len; + } + if (tag == 0x88 && type == 4 && (thumb_offset = get4())) + thumb_offset += base; + if (tag == 0x89 && type == 4) + thumb_length = get4(); + if (tag == 0x8c) + curve_offset = ftell(ifp) + 2112; + if (tag == 0x96) + curve_offset = ftell(ifp) + 2; + if (tag == 0x97) { + for (i=0; i < 4; i++) + ver97 = (ver97 << 4) + fgetc(ifp)-'0'; + switch (ver97) { + case 0x100: + fseek (ifp, 68, SEEK_CUR); + FORC4 cam_mul[(c >> 1) | ((c & 1) << 1)] = get2(); + break; + case 0x102: + fseek (ifp, 6, SEEK_CUR); + goto get2_rggb; + case 0x103: + fseek (ifp, 16, SEEK_CUR); + FORC4 cam_mul[c] = get2(); + } + if (ver97 >> 8 == 2) { + if (ver97 != 0x205) fseek (ifp, 280, SEEK_CUR); + fread (buf97, 324, 1, ifp); + } + } + if (tag == 0xa7 && ver97 >> 8 == 2) { + ci = xlat[0][serial & 0xff]; + cj = xlat[1][fgetc(ifp)^fgetc(ifp)^fgetc(ifp)^fgetc(ifp)]; + ck = 0x60; + for (i=0; i < 324; i++) + buf97[i] ^= (cj += ci * ck++); + FORC4 cam_mul[c ^ (c >> 1)] = + sget2 (buf97 + (ver97 == 0x205 ? 14:6) + c*2); + } + if (tag == 0x200 && len == 4) + black = (get2()+get2()+get2()+get2())/4; + if (tag == 0x201 && len == 4) + goto get2_rggb; + if (tag == 0x401 && len == 4) { + black = (get4()+get4()+get4()+get4())/4; + } + if (tag == 0xe01) { /* Nikon Capture Note */ + type = order; + order = 0x4949; + fseek (ifp, 22, SEEK_CUR); + for (offset=22; offset+22 < len; offset += 22+i) { + tag = get4(); + fseek (ifp, 14, SEEK_CUR); + i = get4()-4; + if (tag == 0x76a43207) flip = get2(); + else fseek (ifp, i, SEEK_CUR); + } + order = type; + } + if (tag == 0xe80 && len == 256 && type == 7) { + fseek (ifp, 48, SEEK_CUR); + cam_mul[0] = get2() * 508 * 1.078 / 0x10000; + cam_mul[2] = get2() * 382 * 1.173 / 0x10000; + } + if (tag == 0xf00 && type == 7) { + if (len == 614) + fseek (ifp, 176, SEEK_CUR); + else if (len == 734 || len == 1502) + fseek (ifp, 148, SEEK_CUR); + else goto next; + goto get2_256; + } + if (tag == 0x1011 && len == 9 && use_camera_wb) { + for (i=0; i < 3; i++) + FORC3 rgb_cam[i][c] = ((short) get2()) / 256.0; + raw_color = rgb_cam[0][0] < 1; + } + if (tag == 0x1017) + cam_mul[0] = get2() / 256.0; + if (tag == 0x1018) + cam_mul[2] = get2() / 256.0; + if (tag == 0x2011 && len == 2) { +get2_256: + order = 0x4d4d; + cam_mul[0] = get2() / 256.0; + cam_mul[2] = get2() / 256.0; + } + if (tag == 0x2020) + parse_thumb_note (base, 257, 258); + if (tag == 0xb028) { + fseek (ifp, get4(), SEEK_SET); + parse_thumb_note (base, 136, 137); + } + if (tag == 0x4001) { + i = len == 582 ? 50 : len == 653 ? 68 : len == 796 ? 126 : 0; + fseek (ifp, i, SEEK_CUR); +get2_rggb: + FORC4 cam_mul[c ^ (c >> 1)] = get2(); + } +next: + fseek (ifp, save, SEEK_SET); + } +quit: + order = sorder; +} + +/* + Since the TIFF DateTime string has no timezone information, + assume that the camera's clock was set to Universal Time. + */ +void CLASS get_timestamp (int reversed) +{ + struct tm t; + char str[20]; + int i; + + if (timestamp) return; + str[19] = 0; + if (reversed) + for (i=19; i--; ) str[i] = fgetc(ifp); + else + fread (str, 19, 1, ifp); + memset (&t, 0, sizeof t); + if (sscanf (str, "%d:%d:%d %d:%d:%d", &t.tm_year, &t.tm_mon, + &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec) != 6) + return; + t.tm_year -= 1900; + t.tm_mon -= 1; + if (mktime(&t) > 0) + timestamp = mktime(&t); +} + +void CLASS parse_exif (int base) +{ + unsigned kodak, entries, tag, type, len, save; + double expo; + + kodak = !strncmp(make,"EASTMAN",7); + entries = get2(); + while (entries--) { + tiff_get (base, &tag, &type, &len, &save); + switch (tag) { + case 33434: shutter = getrat(); break; + case 33437: aperture = getrat(); break; + case 34855: iso_speed = get2(); break; + case 36867: + case 36868: get_timestamp(0); break; + case 37377: if ((expo = -getrat()) < 128) + shutter = pow (2, expo); break; + case 37378: aperture = pow (2, getrat()/2); break; + case 37386: focal_len = getrat(); break; + case 37500: parse_makernote (base); break; + case 40962: if (kodak) raw_width = get4(); break; + case 40963: if (kodak) raw_height = get4(); break; + } + fseek (ifp, save, SEEK_SET); + } +} + +void CLASS romm_coeff (float romm_cam[3][3]) +{ + static const float rgb_romm[3][3] = /* ROMM == Kodak ProPhoto */ + { { 2.034193, -0.727420, -0.306766 }, + { -0.228811, 1.231729, -0.002922 }, + { -0.008565, -0.153273, 1.161839 } }; + int i, j, k; + + for (raw_color = i=0; i < 3; i++) + for (j=0; j < 3; j++) + for (rgb_cam[i][j] = k=0; k < 3; k++) + rgb_cam[i][j] += rgb_romm[i][k] * romm_cam[k][j]; +} + +void CLASS parse_mos (int offset) +{ + char data[40]; + int skip, from, i, c, neut[4], planes=0, frot=0; + static const char *mod[] = + { "","DCB2","Volare","Cantare","CMost","Valeo 6","Valeo 11","Valeo 22", + "","Valeo 17","","Aptus 17","Aptus 22","Aptus 75","Aptus 65" }; + float romm_cam[3][3]; + + fseek (ifp, offset, SEEK_SET); + while (1) { + if (get4() != 0x504b5453) break; + get4(); + fread (data, 1, 40, ifp); + skip = get4(); + from = ftell(ifp); + if (!strcmp(data,"JPEG_preview_data")) { + thumb_offset = from; + thumb_length = skip; + } + if (!strcmp(data,"icc_camera_profile")) { + profile_offset = from; + profile_length = skip; + } + if (!strcmp(data,"ShootObj_back_type")) { + fscanf (ifp, "%d", &i); + if ((unsigned) i < sizeof mod / sizeof (*mod)) + strcpy (model, mod[i]); + } + if (!strcmp(data,"CaptProf_color_matrix") && use_camera_wb) { + for (i=0; i < 9; i++) + fscanf (ifp, "%f", &romm_cam[0][i]); + romm_coeff (romm_cam); + } + if (!strcmp(data,"CaptProf_number_of_planes")) + fscanf (ifp, "%d", &planes); + if (!strcmp(data,"CaptProf_raw_data_rotation")) + fscanf (ifp, "%d", &flip); + if (!strcmp(data,"CaptProf_mosaic_pattern")) + FORC4 { + fscanf (ifp, "%d", &i); + if (i == 1) frot = c ^ (c >> 1); + } + if (!strcmp(data,"ImgProf_rotation_angle")) { + fscanf (ifp, "%d", &i); + flip = i - flip; + } + if (!strcmp(data,"NeutObj_neutrals") && !cam_mul[0]) { + FORC4 fscanf (ifp, "%d", neut+c); + FORC3 cam_mul[c] = (float) neut[0] / neut[c+1]; + } + parse_mos (from); + fseek (ifp, skip+from, SEEK_SET); + } + if (planes) + filters = (planes == 1) * 0x01010101 * + (uchar) "\x94\x61\x16\x49"[(flip/90 + frot) & 3]; +} + +void CLASS linear_table (unsigned len) +{ + int i; + if (len > 0x1000) len = 0x1000; + read_shorts (curve, len); + for (i=len; i < 0x1000; i++) + curve[i] = curve[i-1]; + maximum = curve[0xfff]; +} + +void CLASS parse_kodak_ifd (int base) +{ + unsigned entries, tag, type, len, save; + int i, c, wbi=-2, wbtemp=6500; + float mul[3], num; + + entries = get2(); + if (entries > 1024) return; + while (entries--) { + tiff_get (base, &tag, &type, &len, &save); + if (tag == 1020) wbi = getint(type); + if (tag == 1021 && len == 72) { /* WB set in software */ + fseek (ifp, 40, SEEK_CUR); + FORC3 cam_mul[c] = 2048.0 / get2(); + wbi = -2; + } + if (tag == 2118) wbtemp = getint(type); + if (tag == 2130 + wbi) + FORC3 mul[c] = getreal(type); + if (tag == 2140 + wbi && wbi >= 0) + FORC3 { + for (num=i=0; i < 4; i++) + num += getreal(type) * pow (wbtemp/100.0, i); + cam_mul[c] = 2048 / (num * mul[c]); + } + if (tag == 2317) linear_table (len); + if (tag == 6020) iso_speed = getint(type); + fseek (ifp, save, SEEK_SET); + } +} + +void CLASS parse_minolta (int base); + +int CLASS parse_tiff_ifd (int base, int level) +{ + unsigned entries, tag, type, len, plen=16, save; + int ifd, use_cm=0, cfa, i, j, c, ima_len=0; + char software[64], *cbuf, *cp; + uchar cfa_pat[16], cfa_pc[] = { 0,1,2,3 }, tab[256]; + double dblack, cc[4][4], cm[4][3], cam_xyz[4][3], num, sx, sy; + double ab[]={ 1,1,1,1 }, asn[] = { 0,0,0,0 }, xyz[] = { 1,1,1 }; + unsigned *buf, sony_offset=0, sony_length=0, sony_key=0; + struct jhead jh; + FILE *sfp; + + if (tiff_nifds >= sizeof tiff_ifd / sizeof tiff_ifd[0]) + return 1; + ifd = tiff_nifds++; + for (j=0; j < 4; j++) + for (i=0; i < 4; i++) + cc[j][i] = i == j; + entries = get2(); + if (entries > 512) return 1; + while (entries--) { + tiff_get (base, &tag, &type, &len, &save); + switch (tag) { + case 17: case 18: + if (type == 3 && len == 1) + cam_mul[(tag-17)*2] = get2() / 256.0; + break; + case 23: + if (type == 3) iso_speed = get2(); + break; + case 36: case 37: case 38: + cam_mul[tag-0x24] = get2(); + break; + case 39: + if (len < 50 || cam_mul[0]) break; + fseek (ifp, 12, SEEK_CUR); + FORC3 cam_mul[c] = get2(); + break; + case 2: case 256: /* ImageWidth */ + tiff_ifd[ifd].width = getint(type); + break; + case 3: case 257: /* ImageHeight */ + tiff_ifd[ifd].height = getint(type); + break; + case 258: /* BitsPerSample */ + tiff_ifd[ifd].samples = len; + tiff_ifd[ifd].bps = get2(); + break; + case 259: /* Compression */ + tiff_ifd[ifd].comp = get2(); + break; + case 262: /* PhotometricInterpretation */ + tiff_ifd[ifd].phint = get2(); + break; + case 271: /* Make */ + fgets (make, 64, ifp); + break; + case 272: /* Model */ + fgets (model, 64, ifp); + break; + case 273: /* StripOffset */ + case 513: + tiff_ifd[ifd].offset = get4()+base; + if (!tiff_ifd[ifd].width) { + fseek (ifp, tiff_ifd[ifd].offset, SEEK_SET); + if (ljpeg_start (&jh, 1)) { + tiff_ifd[ifd].comp = 6; + tiff_ifd[ifd].width = jh.wide << (jh.clrs == 2); + tiff_ifd[ifd].height = jh.high; + tiff_ifd[ifd].bps = jh.bits; + tiff_ifd[ifd].samples = jh.clrs; + } + } + break; + case 274: /* Orientation */ + tiff_ifd[ifd].flip = "50132467"[get2() & 7]-'0'; + break; + case 277: /* SamplesPerPixel */ + tiff_ifd[ifd].samples = getint(type); + break; + case 279: /* StripByteCounts */ + case 514: + tiff_ifd[ifd].bytes = get4(); + break; + case 305: /* Software */ + fgets (software, 64, ifp); + if (!strncmp(software,"Adobe",5) || + !strncmp(software,"dcraw",5) || + !strncmp(software,"Bibble",6) || + !strcmp (software,"Digital Photo Professional")) + is_raw = 0; + break; + case 306: /* DateTime */ + get_timestamp(0); + break; + case 323: /* TileLength */ + tile_length = getint(type); + break; + case 324: /* TileOffsets */ + tiff_ifd[ifd].offset = len > 1 ? ftell(ifp) : get4(); + break; + case 330: /* SubIFDs */ + if (!strcmp(model,"DSLR-A100") && tiff_ifd[ifd].width == 3872) { + data_offset = get4()+base; + ifd++; break; + } + while (len--) { + i = ftell(ifp); + fseek (ifp, get4()+base, SEEK_SET); + if (parse_tiff_ifd (base, level+1)) break; + fseek (ifp, i+4, SEEK_SET); + } + break; + case 400: + strcpy (make, "Sarnoff"); + maximum = 0xfff; + break; + case 29184: sony_offset = get4(); break; + case 29185: sony_length = get4(); break; + case 29217: sony_key = get4(); break; + case 29443: + FORC4 cam_mul[c ^ (c < 2)] = get2(); + break; + case 33405: /* Model2 */ + fgets (model2, 64, ifp); + break; + case 33422: /* CFAPattern */ + case 64777: /* Kodak P-series */ + if ((plen=len) > 16) plen = 16; + fread (cfa_pat, 1, plen, ifp); + for (colors=cfa=i=0; i < plen; i++) { + colors += !(cfa & (1 << cfa_pat[i])); + cfa |= 1 << cfa_pat[i]; + } + if (cfa == 070) memcpy (cfa_pc,"\003\004\005",3); /* CMY */ + if (cfa == 072) memcpy (cfa_pc,"\005\003\004\001",4); /* GMCY */ + goto guess_cfa_pc; + case 33424: + fseek (ifp, get4()+base, SEEK_SET); + parse_kodak_ifd (base); + break; + case 33434: /* ExposureTime */ + shutter = getrat(); + break; + case 33437: /* FNumber */ + aperture = getrat(); + break; + case 34306: /* Leaf white balance */ + FORC4 cam_mul[c ^ 1] = 4096.0 / get2(); + break; + case 34307: /* Leaf CatchLight color matrix */ + fread (software, 1, 7, ifp); + if (strncmp(software,"MATRIX",6)) break; + colors = 4; + for (raw_color = i=0; i < 3; i++) { + FORC4 fscanf (ifp, "%f", &rgb_cam[i][c^1]); + if (!use_camera_wb) continue; + num = 0; + FORC4 num += rgb_cam[i][c]; + FORC4 rgb_cam[i][c] /= num; + } + break; + case 34310: /* Leaf metadata */ + parse_mos (ftell(ifp)); + case 34303: + strcpy (make, "Leaf"); + break; + case 34665: /* EXIF tag */ + fseek (ifp, get4()+base, SEEK_SET); + parse_exif (base); + break; + case 34675: /* InterColorProfile */ + case 50831: /* AsShotICCProfile */ + profile_offset = ftell(ifp); + profile_length = len; + break; + case 37122: /* CompressedBitsPerPixel */ + kodak_cbpp = get4(); + break; + case 37386: /* FocalLength */ + focal_len = getrat(); + break; + case 37393: /* ImageNumber */ + shot_order = getint(type); + break; + case 37400: /* old Kodak KDC tag */ + for (raw_color = i=0; i < 3; i++) { + getrat(); + FORC3 rgb_cam[i][c] = getrat(); + } + break; + case 46275: /* Imacon tags */ + strcpy (make, "Imacon"); + data_offset = ftell(ifp); + ima_len = len; + break; + case 46279: + fseek (ifp, 78, SEEK_CUR); + raw_width = get4(); + raw_height = get4(); + left_margin = get4() & 7; + width = raw_width - left_margin - (get4() & 7); + top_margin = get4() & 7; + height = raw_height - top_margin - (get4() & 7); + fseek (ifp, 52, SEEK_CUR); + FORC3 cam_mul[c] = getreal(11); + fseek (ifp, 114, SEEK_CUR); + flip = (get2() >> 7) * 90; + if (width * height * 6 == ima_len) { + if (flip % 180 == 90) SWAP(width,height); + filters = flip = 0; + } + break; + case 50454: /* Sinar tag */ + case 50455: + if (!(cbuf = (char *) malloc(len))) break; + fread (cbuf, 1, len, ifp); + for (cp = cbuf-1; cp && cp < cbuf+len; cp = strchr(cp,'\n')) + if (!strncmp (++cp,"Neutral ",8)) + sscanf (cp+8, "%f %f %f", cam_mul, cam_mul+1, cam_mul+2); + free (cbuf); + break; + case 50706: /* DNGVersion */ + FORC4 dng_version = (dng_version << 8) + fgetc(ifp); + break; + case 50710: /* CFAPlaneColor */ + if (len > 4) len = 4; + colors = len; + fread (cfa_pc, 1, colors, ifp); +guess_cfa_pc: + FORCC tab[cfa_pc[c]] = c; + cdesc[c] = 0; + for (i=16; i--; ) + filters = filters << 2 | tab[cfa_pat[i % plen]]; + break; + case 50711: /* CFALayout */ + if (get2() == 2) { + fuji_width = 1; + filters = 0x49494949; + } + break; + case 291: + case 50712: /* LinearizationTable */ + linear_table (len); + break; + case 50714: /* BlackLevel */ + case 50715: /* BlackLevelDeltaH */ + case 50716: /* BlackLevelDeltaV */ + for (dblack=i=0; i < len; i++) + dblack += getreal(type); + black += dblack/len + 0.5; + break; + case 50717: /* WhiteLevel */ + maximum = getint(type); + break; + case 50718: /* DefaultScale */ + sx = getrat(); + sy = getrat(); + if (sx > sy) xmag = sx / sy; + else ymag = sy / sx; + break; + case 50721: /* ColorMatrix1 */ + case 50722: /* ColorMatrix2 */ + FORCC for (j=0; j < 3; j++) + cm[c][j] = getrat(); + use_cm = 1; + break; + case 50723: /* CameraCalibration1 */ + case 50724: /* CameraCalibration2 */ + for (i=0; i < colors; i++) + FORCC cc[i][c] = getrat(); + case 50727: /* AnalogBalance */ + FORCC ab[c] = getrat(); + break; + case 50728: /* AsShotNeutral */ + FORCC asn[c] = getreal(type); + break; + case 50729: /* AsShotWhiteXY */ + xyz[0] = getrat(); + xyz[1] = getrat(); + xyz[2] = 1 - xyz[0] - xyz[1]; + FORC3 xyz[c] /= d65_white[c]; + break; + case 50740: /* DNGPrivateData */ + if (dng_version) break; + i = order; + parse_minolta (j = get4()+base); + order = i; + fseek (ifp, j, SEEK_SET); + parse_tiff_ifd (base, level+1); + break; + case 50752: + read_shorts (cr2_slice, 3); + break; + case 50829: /* ActiveArea */ + top_margin = getint(type); + left_margin = getint(type); + height = getint(type) - top_margin; + width = getint(type) - left_margin; + break; + case 64772: /* Kodak P-series */ + fseek (ifp, 16, SEEK_CUR); + data_offset = get4(); + fseek (ifp, 28, SEEK_CUR); + data_offset += get4(); + load_raw = &CLASS packed_12_load_raw; + } + fseek (ifp, save, SEEK_SET); + } + if (sony_length && (buf = (unsigned *) malloc(sony_length))) { + fseek (ifp, sony_offset, SEEK_SET); + fread (buf, sony_length, 1, ifp); + sony_decrypt (buf, sony_length/4, 1, sony_key); + sfp = ifp; + if ((ifp = tmpfile())) { + fwrite (buf, sony_length, 1, ifp); + fseek (ifp, 0, SEEK_SET); + parse_tiff_ifd (-sony_offset, level); + fclose (ifp); + } + ifp = sfp; + free (buf); + } + for (i=0; i < colors; i++) + FORCC cc[i][c] *= ab[i]; + if (use_cm) { + FORCC for (i=0; i < 3; i++) + for (cam_xyz[c][i]=j=0; j < colors; j++) + cam_xyz[c][i] += cc[c][j] * cm[j][i] * xyz[i]; + cam_xyz_coeff (cam_xyz); + } + if (asn[0]) + FORCC pre_mul[c] = 1 / asn[c]; + if (!use_cm) + FORCC pre_mul[c] /= cc[c][c]; + return 0; +} + +void CLASS parse_tiff (int base) +{ + int doff, max_samp=0, raw=-1, thm=-1, i; + struct jhead jh; + + fseek (ifp, base, SEEK_SET); + order = get2(); + if (order != 0x4949 && order != 0x4d4d) return; + get2(); + memset (tiff_ifd, 0, sizeof tiff_ifd); + tiff_nifds = 0; + while ((doff = get4())) { + fseek (ifp, doff+base, SEEK_SET); + if (parse_tiff_ifd (base, 0)) break; + } + thumb_misc = 16; + if (thumb_offset) { + fseek (ifp, thumb_offset, SEEK_SET); + if (ljpeg_start (&jh, 1)) { + thumb_misc = jh.bits; + thumb_width = jh.wide; + thumb_height = jh.high; + } + } + for (i=0; i < tiff_nifds; i++) { + if (max_samp < tiff_ifd[i].samples) + max_samp = tiff_ifd[i].samples; + if ((tiff_ifd[i].comp != 6 || tiff_ifd[i].samples != 3) && + tiff_ifd[i].width*tiff_ifd[i].height > raw_width*raw_height) { + raw_width = tiff_ifd[i].width; + raw_height = tiff_ifd[i].height; + tiff_bps = tiff_ifd[i].bps; + tiff_compress = tiff_ifd[i].comp; + data_offset = tiff_ifd[i].offset; + tiff_flip = tiff_ifd[i].flip; + tiff_samples = tiff_ifd[i].samples; + fuji_secondary = tiff_samples == 2; + raw = i; + } + } + fuji_width *= (raw_width+1)/2; + if (tiff_ifd[0].flip) tiff_flip = tiff_ifd[0].flip; + if (raw >= 0) + switch (tiff_compress) { + case 0: case 1: + load_raw = tiff_bps > 8 ? + &CLASS unpacked_load_raw : &CLASS eight_bit_load_raw; + if (tiff_ifd[raw].bytes * 5 == raw_width * raw_height * 8) + load_raw = &CLASS olympus_e300_load_raw; + break; + case 6: case 7: case 99: + load_raw = &CLASS lossless_jpeg_load_raw; break; + case 262: + load_raw = &CLASS kodak_262_load_raw; break; + case 32773: + load_raw = &CLASS packed_12_load_raw; break; + case 65000: + switch (tiff_ifd[raw].phint) { + case 2: load_raw = &CLASS kodak_rgb_load_raw; filters = 0; break; + case 6: load_raw = &CLASS kodak_ycbcr_load_raw; filters = 0; break; + case 32803: load_raw = &CLASS kodak_65000_load_raw; + } + } + if (tiff_samples == 3 && tiff_bps == 8) + if (!dng_version) is_raw = 0; + for (i=0; i < tiff_nifds; i++) + if (i != raw && tiff_ifd[i].samples == max_samp && + tiff_ifd[i].width * tiff_ifd[i].height / SQR(tiff_ifd[i].bps+1) > + thumb_width * thumb_height / SQR(thumb_misc+1)) { + thumb_width = tiff_ifd[i].width; + thumb_height = tiff_ifd[i].height; + thumb_offset = tiff_ifd[i].offset; + thumb_length = tiff_ifd[i].bytes; + thumb_misc = tiff_ifd[i].bps; + thm = i; + } + if (thm >= 0) { + thumb_misc |= tiff_ifd[thm].samples << 5; + switch (tiff_ifd[thm].comp) { + case 0: + write_thumb = &CLASS layer_thumb; + break; + case 1: + if (tiff_ifd[thm].bps > 8) + thumb_load_raw = &CLASS kodak_thumb_load_raw; + else + write_thumb = &CLASS ppm_thumb; + break; + case 65000: + thumb_load_raw = tiff_ifd[thm].phint == 6 ? + &CLASS kodak_ycbcr_load_raw : &CLASS kodak_rgb_load_raw; + } + } +} + +void CLASS parse_minolta (int base) +{ + int save, tag, len, offset, high=0, wide=0, i, c; + + fseek (ifp, base, SEEK_SET); + if (fgetc(ifp) || fgetc(ifp)-'M' || fgetc(ifp)-'R') return; + order = fgetc(ifp) * 0x101; + offset = base + get4() + 8; + while ((save=ftell(ifp)) < offset) { + for (tag=i=0; i < 4; i++) + tag = tag << 8 | fgetc(ifp); + len = get4(); + switch (tag) { + case 0x505244: /* PRD */ + fseek (ifp, 8, SEEK_CUR); + high = get2(); + wide = get2(); + break; + case 0x574247: /* WBG */ + get4(); + i = strstr(model,"A200") ? 3:0; + FORC4 cam_mul[c ^ (c >> 1) ^ i] = get2(); + break; + case 0x545457: /* TTW */ + parse_tiff (ftell(ifp)); + data_offset = offset; + } + fseek (ifp, save+len+8, SEEK_SET); + } + raw_height = high; + raw_width = wide; +} + +/* + Many cameras have a "debug mode" that writes JPEG and raw + at the same time. The raw file has no header, so try to + to open the matching JPEG file and read its metadata. + */ +void CLASS parse_external_jpeg() +{ + char *file, *ext, *jname, *jfile, *jext; + FILE *save=ifp; + + ext = strrchr (ifname, '.'); + file = strrchr (ifname, '/'); + if (!file) file = strrchr (ifname, '\\'); + if (!file) file = ifname-1; + file++; + if (!ext || strlen(ext) != 4 || ext-file != 8) return; + jname = (char *) malloc (strlen(ifname) + 1); + merror (jname, "parse_external()"); + strcpy (jname, ifname); + jfile = file - ifname + jname; + jext = ext - ifname + jname; + if (strcasecmp (ext, ".jpg")) { + strcpy (jext, isupper(ext[1]) ? ".JPG":".jpg"); + memcpy (jfile, file+4, 4); + memcpy (jfile+4, file, 4); + } else + while (isdigit(*--jext)) { + if (*jext != '9') { + (*jext)++; + break; + } + *jext = '0'; + } + if (strcmp (jname, ifname)) { + if ((ifp = fopen (jname, "rb"))) { + if (verbose) + fprintf (stderr, "Reading metadata from %s...\n", jname); + parse_tiff (12); + thumb_offset = 0; + is_raw = 1; + fclose (ifp); + } + } + if (!timestamp) + fprintf (stderr, "Failed to read metadata from %s\n", jname); + free (jname); + ifp = save; +} + +/* + CIFF block 0x1030 contains an 8x8 white sample. + Load this into white[][] for use in scale_colors(). + */ +void CLASS ciff_block_1030() +{ + static const ushort key[] = { 0x410, 0x45f3 }; + int i, bpp, row, col, vbits=0; + unsigned long bitbuf=0; + + if ((get2(),get4()) != 0x80008 || !get4()) return; + bpp = get2(); + if (bpp != 10 && bpp != 12) return; + for (i=row=0; row < 8; row++) + for (col=0; col < 8; col++) { + if (vbits < bpp) { + bitbuf = bitbuf << 16 | (get2() ^ key[i++ & 1]); + vbits += 16; + } + white[row][col] = + bitbuf << (LONG_BIT - vbits) >> (LONG_BIT - bpp); + vbits -= bpp; + } +} + +/* + Parse a CIFF file, better known as Canon CRW format. + */ +void CLASS parse_ciff (int offset, int length) +{ + int tboff, nrecs, c, type, len, save, wbi=-1; + ushort key[] = { 0x410, 0x45f3 }; + + fseek (ifp, offset+length-4, SEEK_SET); + tboff = get4() + offset; + fseek (ifp, tboff, SEEK_SET); + nrecs = get2(); + if (nrecs > 100) return; + while (nrecs--) { + type = get2(); + len = get4(); + save = ftell(ifp) + 4; + fseek (ifp, offset+get4(), SEEK_SET); + if ((((type >> 8) + 8) | 8) == 0x38) + parse_ciff (ftell(ifp), len); /* Parse a sub-table */ + + if (type == 0x080a) { + fread (make, 64, 1, ifp); + fseek (ifp, strlen(make) - 63, SEEK_CUR); + fread (model, 64, 1, ifp); + } + if (type == 0x1810) { + fseek (ifp, 12, SEEK_CUR); + flip = get4(); + } + if (type == 0x1835) /* Get the decoder table */ + tiff_compress = get4(); + if (type == 0x2007) { + thumb_offset = ftell(ifp); + thumb_length = len; + } + if (type == 0x1818) { + shutter = pow (2, -int_to_float((get4(),get4()))); + aperture = pow (2, int_to_float(get4())/2); + } + if (type == 0x102a) { + iso_speed = pow (2, (get4(),get2())/32.0 - 4) * 50; + aperture = pow (2, (get2(),(short)get2())/64.0); + shutter = pow (2,-((short)get2())/32.0); + wbi = (get2(),get2()); + if (wbi > 17) wbi = 0; + fseek (ifp, 32, SEEK_CUR); + if (shutter > 1e6) shutter = get2()/10.0; + } + if (type == 0x102c) { + if (get2() > 512) { /* Pro90, G1 */ + fseek (ifp, 118, SEEK_CUR); + FORC4 cam_mul[c ^ 2] = get2(); + } else { /* G2, S30, S40 */ + fseek (ifp, 98, SEEK_CUR); + FORC4 cam_mul[c ^ (c >> 1) ^ 1] = get2(); + } + } + if (type == 0x0032) { + if (len == 768) { /* EOS D30 */ + fseek (ifp, 72, SEEK_CUR); + FORC4 cam_mul[c ^ (c >> 1)] = 1024.0 / get2(); + if (!wbi) cam_mul[0] = -1; /* use my auto white balance */ + } else if (!cam_mul[0]) { + if (get2() == key[0]) /* Pro1, G6, S60, S70 */ + c = (strstr(model,"Pro1") ? + "012346000000000000":"01345:000000006008")[wbi]-'0'+ 2; + else { /* G3, G5, S45, S50 */ + c = "023457000000006000"[wbi]-'0'; + key[0] = key[1] = 0; + } + fseek (ifp, 78 + c*8, SEEK_CUR); + FORC4 cam_mul[c ^ (c >> 1) ^ 1] = get2() ^ key[c & 1]; + if (!wbi) cam_mul[0] = -1; + } + } + if (type == 0x10a9) { /* D60, 10D, 300D, and clones */ + if (len > 66) wbi = "0134567028"[wbi]-'0'; + fseek (ifp, 2 + wbi*8, SEEK_CUR); + FORC4 cam_mul[c ^ (c >> 1)] = get2(); + } + if (type == 0x1030 && (0x18040 >> wbi & 1)) + ciff_block_1030(); /* all that don't have 0x10a9 */ + if (type == 0x1031) { + raw_width = (get2(),get2()); + raw_height = get2(); + } + if (type == 0x5029) { + focal_len = len >> 16; + if ((len & 0xffff) == 2) focal_len /= 32; + } + if (type == 0x5813) flash_used = int_to_float(len); + if (type == 0x5814) canon_ev = int_to_float(len); + if (type == 0x5817) shot_order = len; + if (type == 0x5834) unique_id = len; + if (type == 0x580e) timestamp = len; + if (type == 0x180e) timestamp = get4(); +#ifdef LOCALTIME + if ((type | 0x4000) == 0x580e) + timestamp = mktime (gmtime (×tamp)); +#endif + fseek (ifp, save, SEEK_SET); + } +} + +void CLASS parse_rollei() +{ + char line[128], *val; + struct tm t; + + fseek (ifp, 0, SEEK_SET); + memset (&t, 0, sizeof t); + do { + fgets (line, 128, ifp); + if ((val = strchr(line,'='))) + *val++ = 0; + else + val = line + strlen(line); + if (!strcmp(line,"DAT")) + sscanf (val, "%d.%d.%d", &t.tm_mday, &t.tm_mon, &t.tm_year); + if (!strcmp(line,"TIM")) + sscanf (val, "%d:%d:%d", &t.tm_hour, &t.tm_min, &t.tm_sec); + if (!strcmp(line,"HDR")) + thumb_offset = atoi(val); + if (!strcmp(line,"X ")) + raw_width = atoi(val); + if (!strcmp(line,"Y ")) + raw_height = atoi(val); + if (!strcmp(line,"TX ")) + thumb_width = atoi(val); + if (!strcmp(line,"TY ")) + thumb_height = atoi(val); + } while (strncmp(line,"EOHD",4)); + data_offset = thumb_offset + thumb_width * thumb_height * 2; + t.tm_year -= 1900; + t.tm_mon -= 1; + if (mktime(&t) > 0) + timestamp = mktime(&t); + strcpy (make, "Rollei"); + strcpy (model,"d530flex"); + write_thumb = &CLASS rollei_thumb; +} + +void CLASS parse_phase_one (int base) +{ + unsigned entries, tag, type, len, data, save, i, c; + float romm_cam[3][3]; + char *cp; + + memset (&ph1, 0, sizeof ph1); + fseek (ifp, base, SEEK_SET); + order = get4() & 0xffff; + if (get4() >> 8 != 0x526177) return; /* "Raw" */ + fseek (ifp, base+get4(), SEEK_SET); + entries = get4(); + get4(); + while (entries--) { + tag = get4(); + type = get4(); + len = get4(); + data = get4(); + save = ftell(ifp); + fseek (ifp, base+data, SEEK_SET); + switch (tag) { + case 0x100: flip = "0653"[data & 3]-'0'; break; + case 0x106: + for (i=0; i < 9; i++) + romm_cam[0][i] = getreal(11); + romm_coeff (romm_cam); + break; + case 0x107: + FORC3 cam_mul[c] = pre_mul[c] = getreal(11); + break; + case 0x108: raw_width = data; break; + case 0x109: raw_height = data; break; + case 0x10a: left_margin = data; break; + case 0x10b: top_margin = data; break; + case 0x10c: width = data; break; + case 0x10d: height = data; break; + case 0x10e: ph1.format = data; break; + case 0x10f: data_offset = data+base; break; + case 0x110: meta_offset = data+base; + meta_length = len; break; + case 0x112: ph1.key_off = save - 4; break; + case 0x210: ph1.tag_210 = int_to_float(data); break; + case 0x21a: ph1.tag_21a = data; break; + case 0x21c: strip_offset = data+base; break; + case 0x21d: ph1.black = data; break; + case 0x222: ph1.split_col = data - left_margin; break; + case 0x223: ph1.black_off = data+base; break; + case 0x301: + model[63] = 0; + fread (model, 1, 63, ifp); + if ((cp = strstr(model," camera"))) *cp = 0; + } + fseek (ifp, save, SEEK_SET); + } + load_raw = ph1.format < 3 ? + &CLASS phase_one_load_raw : &CLASS phase_one_load_raw_c; + maximum = 0xffff; + strcpy (make, "Phase One"); + if (model[0]) return; + switch (raw_height) { + case 2060: strcpy (model,"LightPhase"); break; + case 2682: strcpy (model,"H 10"); break; + case 4128: strcpy (model,"H 20"); break; + case 5488: strcpy (model,"H 25"); break; + } +} + +void CLASS parse_fuji (int offset) +{ + unsigned entries, tag, len, save, c; + + fseek (ifp, offset, SEEK_SET); + entries = get4(); + if (entries > 255) return; + while (entries--) { + tag = get2(); + len = get2(); + save = ftell(ifp); + if (tag == 0x100) { + raw_height = get2(); + raw_width = get2(); + } else if (tag == 0x121) { + height = get2(); + if ((width = get2()) == 4284) width += 3; + } else if (tag == 0x130) + fuji_layout = fgetc(ifp) >> 7; + if (tag == 0x2ff0) + FORC4 cam_mul[c ^ 1] = get2(); + fseek (ifp, save+len, SEEK_SET); + } + if (fuji_layout) { + height *= 2; + width /= 2; + } +} + +int CLASS parse_jpeg (int offset) +{ + int len, save, hlen, mark; + + fseek (ifp, offset, SEEK_SET); + if (fgetc(ifp) != 0xff || fgetc(ifp) != 0xd8) return 0; + + while (fgetc(ifp) == 0xff && (mark = fgetc(ifp)) != 0xda) { + order = 0x4d4d; + len = get2() - 2; + save = ftell(ifp); + if (mark == 0xc0 || mark == 0xc3) { + fgetc(ifp); + raw_height = get2(); + raw_width = get2(); + } + order = get2(); + hlen = get4(); + if (get4() == 0x48454150) /* "HEAP" */ + parse_ciff (save+hlen, len-hlen); + parse_tiff (save+6); + fseek (ifp, save+len, SEEK_SET); + } + return 1; +} + +void CLASS parse_riff() +{ + unsigned i, size, end; + char tag[4], date[64], month[64]; + static const char mon[12][4] = + { "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec" }; + struct tm t; + + order = 0x4949; + fread (tag, 4, 1, ifp); + size = get4(); + if (!memcmp(tag,"RIFF",4) || !memcmp(tag,"LIST",4)) { + end = ftell(ifp) + size; + get4(); + while (ftell(ifp) < end) + parse_riff(); + } else if (!memcmp(tag,"IDIT",4) && size < 64) { + fread (date, 64, 1, ifp); + date[size] = 0; + memset (&t, 0, sizeof t); + if (sscanf (date, "%*s %s %d %d:%d:%d %d", month, &t.tm_mday, + &t.tm_hour, &t.tm_min, &t.tm_sec, &t.tm_year) == 6) { + for (i=0; i < 12 && strcmp(mon[i],month); i++); + t.tm_mon = i; + t.tm_year -= 1900; + if (mktime(&t) > 0) + timestamp = mktime(&t); + } + } else + fseek (ifp, size, SEEK_CUR); +} + +void CLASS parse_smal (int offset, int fsize) +{ + int ver; + + fseek (ifp, offset+2, SEEK_SET); + order = 0x4949; + ver = fgetc(ifp); + if (ver == 6) + fseek (ifp, 5, SEEK_CUR); + if (get4() != fsize) return; + if (ver > 6) data_offset = get4(); + raw_height = height = get2(); + raw_width = width = get2(); + strcpy (make, "SMaL"); + sprintf (model, "v%d %dx%d", ver, width, height); + if (ver == 6) load_raw = &CLASS smal_v6_load_raw; + if (ver == 9) load_raw = &CLASS smal_v9_load_raw; +} + +char * CLASS foveon_gets (int offset, char *str, int len) +{ + int i; + fseek (ifp, offset, SEEK_SET); + for (i=0; i < len-1; i++) + if ((str[i] = get2()) == 0) break; + str[i] = 0; + return str; +} + +void CLASS parse_foveon() +{ + int entries, img=0, off, len, tag, save, i, wide, high, pent, poff[256][2]; + char name[64], value[64]; + + order = 0x4949; /* Little-endian */ + fseek (ifp, 36, SEEK_SET); + flip = get4(); + fseek (ifp, -4, SEEK_END); + fseek (ifp, get4(), SEEK_SET); + if (get4() != 0x64434553) return; /* SECd */ + entries = (get4(),get4()); + while (entries--) { + off = get4(); + len = get4(); + tag = get4(); + save = ftell(ifp); + fseek (ifp, off, SEEK_SET); + if (get4() != (0x20434553 | (tag << 24))) return; + switch (tag) { + case 0x47414d49: /* IMAG */ + case 0x32414d49: /* IMA2 */ + fseek (ifp, 12, SEEK_CUR); + wide = get4(); + high = get4(); + if (wide > raw_width && high > raw_height) { + raw_width = wide; + raw_height = high; + data_offset = off+24; + } + fseek (ifp, off+28, SEEK_SET); + if (fgetc(ifp) == 0xff && fgetc(ifp) == 0xd8) { + thumb_offset = off+28; + thumb_length = len-28; + } + if (++img == 2 && !thumb_length) { + thumb_offset = off+24; + thumb_width = wide; + thumb_height = high; + write_thumb = &CLASS foveon_thumb; + } + break; + case 0x464d4143: /* CAMF */ + meta_offset = off+24; + meta_length = len-28; + if (meta_length > 0x20000) + meta_length = 0x20000; + break; + case 0x504f5250: /* PROP */ + pent = (get4(),get4()); + fseek (ifp, 12, SEEK_CUR); + off += pent*8 + 24; + if (pent > 256) pent=256; + for (i=0; i < pent*2; i++) + poff[0][i] = off + get4()*2; + for (i=0; i < pent; i++) { + foveon_gets (poff[i][0], name, 64); + foveon_gets (poff[i][1], value, 64); + if (!strcmp (name, "ISO")) + iso_speed = atoi(value); + if (!strcmp (name, "CAMMANUF")) + strcpy (make, value); + if (!strcmp (name, "CAMMODEL")) + strcpy (model, value); + if (!strcmp (name, "WB_DESC")) + strcpy (model2, value); + if (!strcmp (name, "TIME")) + timestamp = atoi(value); + if (!strcmp (name, "EXPTIME")) + shutter = atoi(value) / 1000000.0; + if (!strcmp (name, "APERTURE")) + aperture = atof(value); + if (!strcmp (name, "FLENGTH")) + focal_len = atof(value); + } +#ifdef LOCALTIME + timestamp = mktime (gmtime (×tamp)); +#endif + } + fseek (ifp, save, SEEK_SET); + } + is_foveon = 1; +} + +/* + Thanks to Adobe for providing these excellent CAM -> XYZ matrices! + */ +void CLASS adobe_coeff (char *make, char *model) +{ + static const struct { + const char *prefix; + short black, trans[12]; + } table[] = { + { "Canon EOS D2000", 0, + { 24542,-10860,-3401,-1490,11370,-297,2858,-605,3225 } }, + { "Canon EOS D6000", 0, + { 20482,-7172,-3125,-1033,10410,-285,2542,226,3136 } }, + { "Canon EOS D30", 0, + { 9805,-2689,-1312,-5803,13064,3068,-2438,3075,8775 } }, + { "Canon EOS D60", 0, + { 6188,-1341,-890,-7168,14489,2937,-2640,3228,8483 } }, + { "Canon EOS 5D", 0, + { 6347,-479,-972,-8297,15954,2480,-1968,2131,7649 } }, + { "Canon EOS 20Da", 0, + { 14155,-5065,-1382,-6550,14633,2039,-1623,1824,6561 } }, + { "Canon EOS 20D", 0, + { 6599,-537,-891,-8071,15783,2424,-1983,2234,7462 } }, + { "Canon EOS 30D", 0, + { 6257,-303,-1000,-7880,15621,2396,-1714,1904,7046 } }, + { "Canon EOS 350D", 0, + { 6018,-617,-965,-8645,15881,2975,-1530,1719,7642 } }, + { "Canon EOS-1Ds Mark II", 0, + { 6517,-602,-867,-8180,15926,2378,-1618,1771,7633 } }, + { "Canon EOS-1D Mark II N", 0, + { 6240,-466,-822,-8180,15825,2500,-1801,1938,8042 } }, + { "Canon EOS-1D Mark II", 0, + { 6264,-582,-724,-8312,15948,2504,-1744,1919,8664 } }, + { "Canon EOS-1DS", 0, + { 4374,3631,-1743,-7520,15212,2472,-2892,3632,8161 } }, + { "Canon EOS-1D", 0, + { 6806,-179,-1020,-8097,16415,1687,-3267,4236,7690 } }, + { "Canon EOS", 0, + { 8197,-2000,-1118,-6714,14335,2592,-2536,3178,8266 } }, + { "Canon PowerShot A50", 0, + { -5300,9846,1776,3436,684,3939,-5540,9879,6200,-1404,11175,217 } }, + { "Canon PowerShot A5", 0, + { -4801,9475,1952,2926,1611,4094,-5259,10164,5947,-1554,10883,547 } }, + { "Canon PowerShot G1", 0, + { -4778,9467,2172,4743,-1141,4344,-5146,9908,6077,-1566,11051,557 } }, + { "Canon PowerShot G2", 0, + { 9087,-2693,-1049,-6715,14382,2537,-2291,2819,7790 } }, + { "Canon PowerShot G3", 0, + { 9212,-2781,-1073,-6573,14189,2605,-2300,2844,7664 } }, + { "Canon PowerShot G5", 0, + { 9757,-2872,-933,-5972,13861,2301,-1622,2328,7212 } }, + { "Canon PowerShot G6", 0, + { 9877,-3775,-871,-7613,14807,3072,-1448,1305,7485 } }, + { "Canon PowerShot Pro1", 0, + { 10062,-3522,-999,-7643,15117,2730,-765,817,7323 } }, + { "Canon PowerShot Pro70", 34, + { -4155,9818,1529,3939,-25,4522,-5521,9870,6610,-2238,10873,1342 } }, + { "Canon PowerShot Pro90", 0, + { -4963,9896,2235,4642,-987,4294,-5162,10011,5859,-1770,11230,577 } }, + { "Canon PowerShot S30", 0, + { 10566,-3652,-1129,-6552,14662,2006,-2197,2581,7670 } }, + { "Canon PowerShot S40", 0, + { 8510,-2487,-940,-6869,14231,2900,-2318,2829,9013 } }, + { "Canon PowerShot S45", 0, + { 8163,-2333,-955,-6682,14174,2751,-2077,2597,8041 } }, + { "Canon PowerShot S50", 0, + { 8882,-2571,-863,-6348,14234,2288,-1516,2172,6569 } }, + { "Canon PowerShot S60", 0, + { 8795,-2482,-797,-7804,15403,2573,-1422,1996,7082 } }, + { "Canon PowerShot S70", 0, + { 9976,-3810,-832,-7115,14463,2906,-901,989,7889 } }, + { "Contax N Digital", 0, + { 7777,1285,-1053,-9280,16543,2916,-3677,5679,7060 } }, + { "EPSON R-D1", 0, + { 6827,-1878,-732,-8429,16012,2564,-704,592,7145 } }, + { "FUJIFILM FinePix E550", 0, + { 11044,-3888,-1120,-7248,15168,2208,-1531,2277,8069 } }, + { "FUJIFILM FinePix E900", 0, + { 9183,-2526,-1078,-7461,15071,2574,-2022,2440,8639 } }, + { "FUJIFILM FinePix F8", 0, + { 11044,-3888,-1120,-7248,15168,2208,-1531,2277,8069 } }, + { "FUJIFILM FinePix F7", 0, + { 10004,-3219,-1201,-7036,15047,2107,-1863,2565,7736 } }, + { "FUJIFILM FinePix S20Pro", 0, + { 10004,-3219,-1201,-7036,15047,2107,-1863,2565,7736 } }, + { "FUJIFILM FinePix S2Pro", 128, + { 12492,-4690,-1402,-7033,15423,1647,-1507,2111,7697 } }, + { "FUJIFILM FinePix S3Pro", 0, + { 11807,-4612,-1294,-8927,16968,1988,-2120,2741,8006 } }, + { "FUJIFILM FinePix S5000", 0, + { 8754,-2732,-1019,-7204,15069,2276,-1702,2334,6982 } }, + { "FUJIFILM FinePix S5100", 0, + { 11940,-4431,-1255,-6766,14428,2542,-993,1165,7421 } }, + { "FUJIFILM FinePix S5500", 0, + { 11940,-4431,-1255,-6766,14428,2542,-993,1165,7421 } }, + { "FUJIFILM FinePix S5200", 0, + { 9636,-2804,-988,-7442,15040,2589,-1803,2311,8621 } }, + { "FUJIFILM FinePix S5600", 0, + { 9636,-2804,-988,-7442,15040,2589,-1803,2311,8621 } }, + { "FUJIFILM FinePix S7000", 0, + { 10190,-3506,-1312,-7153,15051,2238,-2003,2399,7505 } }, + { "FUJIFILM FinePix S9", 0, + { 10491,-3423,-1145,-7385,15027,2538,-1809,2275,8692 } }, + { "Imacon Ixpress", 0, /* DJC */ + { 7025,-1415,-704,-5188,13765,1424,-1248,2742,6038 } }, + { "KODAK NC2000", 0, /* DJC */ + { 16475,-6903,-1218,-851,10375,477,2505,-7,1020 } }, + { "Kodak DCS315C", 8, + { 17523,-4827,-2510,756,8546,-137,6113,1649,2250 } }, + { "Kodak DCS330C", 8, + { 20620,-7572,-2801,-103,10073,-396,3551,-233,2220 } }, + { "KODAK DCS420", 0, + { 10868,-1852,-644,-1537,11083,484,2343,628,2216 } }, + { "KODAK DCS460", 0, + { 10592,-2206,-967,-1944,11685,230,2206,670,1273 } }, + { "KODAK EOSDCS1", 0, + { 10592,-2206,-967,-1944,11685,230,2206,670,1273 } }, + { "KODAK EOSDCS3B", 0, + { 9898,-2700,-940,-2478,12219,206,1985,634,1031 } }, + { "Kodak DCS520C", 180, + { 24542,-10860,-3401,-1490,11370,-297,2858,-605,3225 } }, + { "Kodak DCS560C", 188, + { 20482,-7172,-3125,-1033,10410,-285,2542,226,3136 } }, + { "Kodak DCS620C", 180, + { 23617,-10175,-3149,-2054,11749,-272,2586,-489,3453 } }, + { "Kodak DCS620X", 185, + { 13095,-6231,154,12221,-21,-2137,895,4602,2258 } }, + { "Kodak DCS660C", 214, + { 18244,-6351,-2739,-791,11193,-521,3711,-129,2802 } }, + { "Kodak DCS720X", 0, + { 11775,-5884,950,9556,1846,-1286,-1019,6221,2728 } }, + { "Kodak DCS760C", 0, + { 16623,-6309,-1411,-4344,13923,323,2285,274,2926 } }, + { "Kodak DCS Pro SLR", 0, + { 5494,2393,-232,-6427,13850,2846,-1876,3997,5445 } }, + { "Kodak DCS Pro 14nx", 0, + { 5494,2393,-232,-6427,13850,2846,-1876,3997,5445 } }, + { "Kodak DCS Pro 14", 0, + { 7791,3128,-776,-8588,16458,2039,-2455,4006,6198 } }, + { "Kodak ProBack645", 0, + { 16414,-6060,-1470,-3555,13037,473,2545,122,4948 } }, + { "Kodak ProBack", 0, + { 21179,-8316,-2918,-915,11019,-165,3477,-180,4210 } }, + { "KODAK P712", 0, + { 9658,-3314,-823,-5163,12695,2768,-1342,1843,6044 } }, + { "KODAK P850", 0, + { 10511,-3836,-1102,-6946,14587,2558,-1481,1792,6246 } }, + { "KODAK P880", 0, + { 12805,-4662,-1376,-7480,15267,2360,-1626,2194,7904 } }, + { "Leaf CMost", 0, + { 3952,2189,449,-6701,14585,2275,-4536,7349,6536 } }, + { "Leaf Valeo 6", 0, + { 3952,2189,449,-6701,14585,2275,-4536,7349,6536 } }, + { "Leaf Aptus 65", 0, + { 7914,1414,-1190,-8777,16582,2280,-2811,4605,5562 } }, + { "Leaf Aptus 75", 0, + { 7914,1414,-1190,-8777,16582,2280,-2811,4605,5562 } }, + { "Leaf", 0, + { 8236,1746,-1314,-8251,15953,2428,-3673,5786,5771 } }, + { "Micron 2010", 110, /* DJC */ + { 16695,-3761,-2151,155,9682,163,3433,951,4904 } }, + { "Minolta DiMAGE 5", 0, + { 8983,-2942,-963,-6556,14476,2237,-2426,2887,8014 } }, + { "Minolta DiMAGE 7Hi", 0, + { 11368,-3894,-1242,-6521,14358,2339,-2475,3056,7285 } }, + { "Minolta DiMAGE 7", 0, + { 9144,-2777,-998,-6676,14556,2281,-2470,3019,7744 } }, + { "Minolta DiMAGE A1", 0, + { 9274,-2547,-1167,-8220,16323,1943,-2273,2720,8340 } }, + { "MINOLTA DiMAGE A200", 0, + { 8560,-2487,-986,-8112,15535,2771,-1209,1324,7743 } }, + { "Minolta DiMAGE A2", 0, + { 9097,-2726,-1053,-8073,15506,2762,-966,981,7763 } }, + { "Minolta DiMAGE Z2", 0, /* DJC */ + { 11280,-3564,-1370,-4655,12374,2282,-1423,2168,5396 } }, + { "MINOLTA DYNAX 5", 0, + { 10284,-3283,-1086,-7957,15762,2316,-829,882,6644 } }, + { "MINOLTA DYNAX 7", 0, + { 10239,-3104,-1099,-8037,15727,2451,-927,925,6871 } }, + { "NIKON D100", 0, + { 5902,-933,-782,-8983,16719,2354,-1402,1455,6464 } }, + { "NIKON D1H", 0, + { 7577,-2166,-926,-7454,15592,1934,-2377,2808,8606 } }, + { "NIKON D1X", 0, + { 7702,-2245,-975,-9114,17242,1875,-2679,3055,8521 } }, + { "NIKON D1", 0, /* multiplied by 2.218750, 1.0, 1.148438 */ + { 16772,-4726,-2141,-7611,15713,1972,-2846,3494,9521 } }, + { "NIKON D2H", 0, + { 5710,-901,-615,-8594,16617,2024,-2975,4120,6830 } }, + { "NIKON D2X", 0, + { 10231,-2769,-1255,-8301,15900,2552,-797,680,7148 } }, + { "NIKON D50", 0, + { 7732,-2422,-789,-8238,15884,2498,-859,783,7330 } }, + { "NIKON D70", 0, + { 7732,-2422,-789,-8238,15884,2498,-859,783,7330 } }, + { "NIKON D80", 0, /* copied from above */ + { 7732,-2422,-789,-8238,15884,2498,-859,783,7330 } }, + { "NIKON D200", 0, + { 8367,-2248,-763,-8758,16447,2422,-1527,1550,8053 } }, + { "NIKON E950", 0, /* DJC */ + { -3746,10611,1665,9621,-1734,2114,-2389,7082,3064,3406,6116,-244 } }, + { "NIKON E995", 0, /* copied from E5000 */ + { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } }, + { "NIKON E2500", 0, + { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } }, + { "NIKON E4300", 0, /* copied from Minolta DiMAGE Z2 */ + { 11280,-3564,-1370,-4655,12374,2282,-1423,2168,5396 } }, + { "NIKON E4500", 0, + { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } }, + { "NIKON E5000", 0, + { -5547,11762,2189,5814,-558,3342,-4924,9840,5949,688,9083,96 } }, + { "NIKON E5400", 0, + { 9349,-2987,-1001,-7919,15766,2266,-2098,2680,6839 } }, + { "NIKON E5700", 0, + { -5368,11478,2368,5537,-113,3148,-4969,10021,5782,778,9028,211 } }, + { "NIKON E8400", 0, + { 7842,-2320,-992,-8154,15718,2599,-1098,1342,7560 } }, + { "NIKON E8700", 0, + { 8489,-2583,-1036,-8051,15583,2643,-1307,1407,7354 } }, + { "NIKON E8800", 0, + { 7971,-2314,-913,-8451,15762,2894,-1442,1520,7610 } }, + { "OLYMPUS C5050", 0, + { 10508,-3124,-1273,-6079,14294,1901,-1653,2306,6237 } }, + { "OLYMPUS C5060", 0, + { 10445,-3362,-1307,-7662,15690,2058,-1135,1176,7602 } }, + { "OLYMPUS C7070", 0, + { 10252,-3531,-1095,-7114,14850,2436,-1451,1723,6365 } }, + { "OLYMPUS C70", 0, + { 10793,-3791,-1146,-7498,15177,2488,-1390,1577,7321 } }, + { "OLYMPUS C80", 0, + { 8606,-2509,-1014,-8238,15714,2703,-942,979,7760 } }, + { "OLYMPUS E-10", 0, + { 12745,-4500,-1416,-6062,14542,1580,-1934,2256,6603 } }, + { "OLYMPUS E-1", 0, + { 11846,-4767,-945,-7027,15878,1089,-2699,4122,8311 } }, + { "OLYMPUS E-20", 0, + { 13173,-4732,-1499,-5807,14036,1895,-2045,2452,7142 } }, + { "OLYMPUS E-300", 0, + { 7828,-1761,-348,-5788,14071,1830,-2853,4518,6557 } }, + { "OLYMPUS E-330", 0, + { 8961,-2473,-1084,-7979,15990,2067,-2319,3035,8249 } }, + { "OLYMPUS E-500", 0, + { 8136,-1968,-299,-5481,13742,1871,-2556,4205,6630 } }, + { "OLYMPUS SP350", 0, + { 12078,-4836,-1069,-6671,14306,2578,-786,939,7418 } }, + { "OLYMPUS SP3", 0, + { 11766,-4445,-1067,-6901,14421,2707,-1029,1217,7572 } }, + { "OLYMPUS SP500UZ", 0, + { 9493,-3415,-666,-5211,12334,3260,-1548,2262,6482 } }, + { "PENTAX *ist DL2", 0, + { 10504,-2438,-1189,-8603,16207,2531,-1022,863,12242 } }, + { "PENTAX *ist DL", 0, + { 10829,-2838,-1115,-8339,15817,2696,-837,680,11939 } }, + { "PENTAX *ist DS2", 0, + { 10504,-2438,-1189,-8603,16207,2531,-1022,863,12242 } }, + { "PENTAX *ist DS", 0, + { 10371,-2333,-1206,-8688,16231,2602,-1230,1116,11282 } }, + { "PENTAX *ist D", 0, + { 9651,-2059,-1189,-8881,16512,2487,-1460,1345,10687 } }, + { "PENTAX K100D", 0, + { 10504,-2438,-1189,-8603,16207,2531,-1022,863,12242 } }, + { "Panasonic DMC-FZ30", 0, + { 10976,-4029,-1141,-7918,15491,2600,-1670,2071,8246 } }, + { "Panasonic DMC-FZ50", 0, + { 7906,-2709,-594,-6231,13351,3220,-1922,2631,6537 } }, + { "Panasonic DMC-LC1", 0, + { 11340,-4069,-1275,-7555,15266,2448,-2960,3426,7685 } }, + { "Panasonic DMC-LX1", 0, + { 10704,-4187,-1230,-8314,15952,2501,-920,945,8927 } }, + { "Panasonic DMC-LX2", 0, + { 7970,-2904,-583,-6252,13395,3191,-1641,2282,6864 } }, + { "SAMSUNG GX-1S", 0, + { 10504,-2438,-1189,-8603,16207,2531,-1022,863,12242 } }, + { "Sinar", 0, /* DJC */ + { 16442,-2956,-2422,-2877,12128,750,-1136,6066,4559 } }, + { "SONY DSC-F828", 491, + { 7924,-1910,-777,-8226,15459,2998,-1517,2199,6818,-7242,11401,3481 } }, + { "SONY DSC-R1", 512, + { 8512,-2641,-694,-8042,15670,2526,-1821,2117,7414 } }, + { "SONY DSC-V3", 0, + { 7511,-2571,-692,-7894,15088,3060,-948,1111,8128 } }, + { "SONY DSLR-A100", 0, + { 9437,-2811,-774,-8405,16215,2290,-710,596,7181 } } + }; + double cam_xyz[4][3]; + char name[130]; + int i, j; + + sprintf (name, "%s %s", make, model); + for (i=0; i < sizeof table / sizeof *table; i++) + if (!strncmp (name, table[i].prefix, strlen(table[i].prefix))) { + if (table[i].black) + black = table[i].black; + for (j=0; j < 12; j++) + cam_xyz[0][j] = table[i].trans[j] / 10000.0; + cam_xyz_coeff (cam_xyz); + break; + } +} + +void CLASS simple_coeff (int index) +{ + static const float table[][12] = { + /* index 0 -- all Foveon cameras */ + { 1.4032,-0.2231,-0.1016,-0.5263,1.4816,0.017,-0.0112,0.0183,0.9113 }, + /* index 1 -- Kodak DC20 and DC25 */ + { 2.25,0.75,-1.75,-0.25,-0.25,0.75,0.75,-0.25,-0.25,-1.75,0.75,2.25 }, + /* index 2 -- Logitech Fotoman Pixtura */ + { 1.893,-0.418,-0.476,-0.495,1.773,-0.278,-1.017,-0.655,2.672 }, + /* index 3 -- Nikon E880, E900, and E990 */ + { -1.936280, 1.800443, -1.448486, 2.584324, + 1.405365, -0.524955, -0.289090, 0.408680, + -1.204965, 1.082304, 2.941367, -1.818705 } + }; + int i, c; + + for (raw_color = i=0; i < 3; i++) + FORCC rgb_cam[i][c] = table[index][i*colors+c]; +} + +short CLASS guess_byte_order (int words) +{ + uchar test[4][2]; + int t=2, msb; + double diff, sum[2] = {0,0}; + + fread (test[0], 2, 2, ifp); + for (words-=2; words--; ) { + fread (test[t], 2, 1, ifp); + for (msb=0; msb < 2; msb++) { + diff = (test[t^2][msb] << 8 | test[t^2][!msb]) + - (test[t ][msb] << 8 | test[t ][!msb]); + sum[msb] += diff*diff; + } + t = (t+1) & 3; + } + return sum[0] < sum[1] ? 0x4d4d : 0x4949; +} + +/* + Identify which camera created this file, and set global variables + accordingly. + */ +void CLASS identify() +{ + char head[32], *cp; + unsigned hlen, fsize, i, c, is_canon; + struct jhead jh; + static const struct { + int fsize; + char make[12], model[15], withjpeg; + } table[] = { + { 62464, "Kodak", "DC20" ,0 }, + { 124928, "Kodak", "DC20" ,0 }, + { 311696, "ST Micro", "STV680 VGA" ,0 }, /* SPYz */ + { 614400, "Kodak", "KAI-0340" ,0 }, + { 787456, "Creative", "PC-CAM 600" ,0 }, + { 1138688, "Minolta", "RD175" ,0 }, + { 3840000, "Foculus", "531C" ,0 }, + { 1447680, "AVT", "F-145C" ,0 }, + { 1920000, "AVT", "F-201C" ,0 }, + { 5067304, "AVT", "F-510C" ,0 }, + { 10134608, "AVT", "F-510C" ,0 }, + { 16157136, "AVT", "F-810C" ,0 }, + { 1409024, "Sony", "XCD-SX910CR",0 }, + { 2818048, "Sony", "XCD-SX910CR",0 }, + { 3884928, "Micron", "2010" ,0 }, + { 6624000, "Pixelink", "A782" ,0 }, + { 13248000, "Pixelink", "A782" ,0 }, + { 6291456, "RoverShot","3320AF" ,0 }, + { 5939200, "OLYMPUS", "C770UZ" ,0 }, + { 1581060, "NIKON", "E900" ,1 }, /* or E900s,E910 */ + { 2465792, "NIKON", "E950" ,1 }, /* or E800,E700 */ + { 2940928, "NIKON", "E2100" ,1 }, /* or E2500 */ + { 4771840, "NIKON", "E990" ,1 }, /* or E995, Oly C3030Z */ + { 4775936, "NIKON", "E3700" ,1 }, /* or Optio 33WR */ + { 5869568, "NIKON", "E4300" ,1 }, /* or DiMAGE Z2 */ + { 5865472, "NIKON", "E4500" ,1 }, + { 7438336, "NIKON", "E5000" ,1 }, /* or E5700 */ + { 1976352, "CASIO", "QV-2000UX" ,1 }, + { 3217760, "CASIO", "QV-3*00EX" ,1 }, + { 6218368, "CASIO", "QV-5700" ,1 }, + { 7530816, "CASIO", "QV-R51" ,1 }, + { 7684000, "CASIO", "QV-4000" ,1 }, + { 4948608, "CASIO", "EX-S100" ,1 }, + { 7542528, "CASIO", "EX-Z50" ,1 }, + { 7753344, "CASIO", "EX-Z55" ,1 }, + { 7426656, "CASIO", "EX-P505" ,1 }, + { 9313536, "CASIO", "EX-P600" ,1 }, + { 10979200, "CASIO", "EX-P700" ,1 }, + { 3178560, "PENTAX", "Optio S" ,1 }, + { 4841984, "PENTAX", "Optio S" ,1 }, + { 6114240, "PENTAX", "Optio S4" ,1 }, /* or S4i */ + { 12582980, "Sinar", "" ,0 }, + { 33292868, "Sinar", "" ,0 }, + { 44390468, "Sinar", "" ,0 } }; + static const char *corp[] = + { "Canon", "NIKON", "EPSON", "KODAK", "Kodak", "OLYMPUS", "PENTAX", + "MINOLTA", "Minolta", "Konica", "CASIO", "Sinar", "Phase One", + "SAMSUNG" }; + + tiff_flip = flip = filters = -1; /* 0 is valid, so -1 is unknown */ + raw_height = raw_width = fuji_width = cr2_slice[0] = 0; + maximum = height = width = top_margin = left_margin = 0; + make[0] = model[0] = model2[0] = cdesc[0] = 0; + iso_speed = shutter = aperture = focal_len = unique_id = 0; + memset (white, 0, sizeof white); + thumb_offset = thumb_length = thumb_width = thumb_height = 0; + load_raw = thumb_load_raw = NULL; + write_thumb = &CLASS jpeg_thumb; + data_offset = meta_length = tiff_bps = tiff_compress = 0; + kodak_cbpp = zero_after_ff = dng_version = fuji_secondary = 0; + timestamp = shot_order = tiff_samples = black = is_foveon = 0; + is_raw = raw_color = use_gamma = xmag = ymag = 1; + tile_length = INT_MAX; + for (i=0; i < 4; i++) { + cam_mul[i] = i == 1; + pre_mul[i] = i < 3; + FORC3 rgb_cam[c][i] = c == i; + } + colors = 3; + tiff_bps = 12; + for (i=0; i < 0x1000; i++) curve[i] = i; + profile_length = 0; + + order = get2(); + hlen = get4(); + fseek (ifp, 0, SEEK_SET); + fread (head, 1, 32, ifp); + fseek (ifp, 0, SEEK_END); + fsize = ftell(ifp); + if ((cp = (char *) memmem (head, 32, "MMMM", 4)) || + (cp = (char *) memmem (head, 32, "IIII", 4))) { + parse_phase_one (cp-head); + if (cp-head) parse_tiff(0); + } else if (order == 0x4949 || order == 0x4d4d) { + if (!memcmp (head+6,"HEAPCCDR",8)) { + data_offset = hlen; + parse_ciff (hlen, fsize - hlen); + } else { + parse_tiff(0); + } + } else if (!memcmp (head,"\xff\xd8\xff\xe1",4) && + !memcmp (head+6,"Exif",4)) { + fseek (ifp, 4, SEEK_SET); + data_offset = 4 + get2(); + fseek (ifp, data_offset, SEEK_SET); + if (fgetc(ifp) != 0xff) + parse_tiff(12); + thumb_offset = 0; + } else if (!memcmp (head,"BM",2) && + head[26] == 1 && head[28] == 16 && head[30] == 0) { + data_offset = 0x1000; + order = 0x4949; + fseek (ifp, 38, SEEK_SET); + if (get4() == 2834 && get4() == 2834 && get4() == 0 && get4() == 4096) { + strcpy (model, "BMQ"); + flip = 3; + goto nucore; + } + } else if (!memcmp (head,"BR",2)) { + strcpy (model, "RAW"); +nucore: + strcpy (make, "Nucore"); + order = 0x4949; + fseek (ifp, 10, SEEK_SET); + data_offset += get4(); + raw_width = (get4(),get4()); + raw_height = get4(); + if (model[0] == 'B' && raw_width == 2597) { + raw_width++; + data_offset -= 0x1000; + } + } else if (!memcmp (head+25,"ARECOYK",7)) { + strcpy (make, "Contax"); + strcpy (model,"N Digital"); + fseek (ifp, 33, SEEK_SET); + get_timestamp(1); + fseek (ifp, 60, SEEK_SET); + FORC4 cam_mul[c ^ (c >> 1)] = get4(); + } else if (!strcmp (head, "PXN")) { + strcpy (make, "Logitech"); + strcpy (model,"Fotoman Pixtura"); + } else if (!memcmp (head,"FUJIFILM",8)) { + fseek (ifp, 84, SEEK_SET); + thumb_offset = get4(); + thumb_length = get4(); + fseek (ifp, 92, SEEK_SET); + parse_fuji (get4()); + if (thumb_offset > 120) { + fseek (ifp, 120, SEEK_SET); + fuji_secondary = (i = get4()) && 1; + if (fuji_secondary && use_secondary) + parse_fuji (i); + } + fseek (ifp, 100, SEEK_SET); + data_offset = get4(); + parse_tiff (thumb_offset+12); + } else if (!memcmp (head,"RIFF",4)) { + fseek (ifp, 0, SEEK_SET); + parse_riff(); + } else if (!memcmp (head,"DSC-Image",9)) + parse_rollei(); + else if (!memcmp (head,"\0MRM",4)) + parse_minolta(0); + else if (!memcmp (head,"FOVb",4)) + parse_foveon(); + else + for (i=0; i < sizeof table / sizeof *table; i++) + if (fsize == table[i].fsize) { + strcpy (make, table[i].make ); + strcpy (model, table[i].model); + if (table[i].withjpeg) + parse_external_jpeg(); + } + if (make[0] == 0) parse_smal (0, fsize); + if (make[0] == 0) parse_jpeg (is_raw = 0); + + for (i=0; i < sizeof corp / sizeof *corp; i++) + if (strstr (make, corp[i])) /* Simplify company names */ + strcpy (make, corp[i]); + if (!strncmp (make,"KODAK",5)) + make[16] = model[16] = 0; + cp = make + strlen(make); /* Remove trailing spaces */ + while (*--cp == ' ') *cp = 0; + cp = model + strlen(model); + while (*--cp == ' ') *cp = 0; + i = strlen(make); /* Remove make from model */ + if (!strncmp (model, make, i) && model[i++] == ' ') + memmove (model, model+i, 64-i); + if (!strncmp (model,"Digital Camera ",15)) + strcpy (model, model+15); + make[63] = model[63] = model2[63] = 0; + if (!is_raw) return; + + if ((raw_height | raw_width) < 0) + raw_height = raw_width = 0; + if (!maximum) maximum = (1 << tiff_bps) - 1; + if (!height) height = raw_height; + if (!width) width = raw_width; + if (fuji_width) { + width = height + fuji_width; + height = width - 1; + xmag = ymag = 1; + } + if (dng_version) { + strcat (model," DNG"); + if (filters == UINT_MAX) filters = 0; + if (!filters) + colors = tiff_samples; + if (tiff_compress == 1) + load_raw = &CLASS adobe_dng_load_raw_nc; + if (tiff_compress == 7) + load_raw = &CLASS adobe_dng_load_raw_lj; + FORC4 cam_mul[c] = pre_mul[c]; + goto dng_skip; + } + +/* We'll try to decode anything from Canon or Nikon. */ + + if ((is_canon = !strcmp(make,"Canon"))) { + load_raw = memcmp (head+6,"HEAPCCDR",8) ? + &CLASS lossless_jpeg_load_raw : &CLASS canon_compressed_load_raw; + maximum = 0xfff; + } + if (!strcmp(make,"NIKON")) + load_raw = nikon_is_compressed() ? + &CLASS nikon_compressed_load_raw : &CLASS nikon_load_raw; + if (!strncmp (make,"OLYMPUS",7)) + height += height & 1; + +/* Set parameters based on camera name (for non-DNG files). */ + + if (is_foveon) { + if (height*2 < width) ymag = 2; + if (height > width) xmag = 2; + filters = 0; + load_raw = &CLASS foveon_load_raw; + simple_coeff(0); + } else if (!strcmp(model,"PowerShot 600")) { + height = 613; + width = 854; + raw_width = 896; + colors = 4; + filters = 0xe1e4e1e4; + load_raw = &CLASS canon_600_load_raw; + } else if (!strcmp(model,"PowerShot A5") || + !strcmp(model,"PowerShot A5 Zoom")) { + height = 773; + width = 960; + raw_width = 992; + colors = 4; + filters = 0x1e4e1e4e; + load_raw = &CLASS canon_a5_load_raw; + } else if (!strcmp(model,"PowerShot A50")) { + height = 968; + width = 1290; + raw_width = 1320; + colors = 4; + filters = 0x1b4e4b1e; + load_raw = &CLASS canon_a5_load_raw; + } else if (!strcmp(model,"PowerShot Pro70")) { + height = 1024; + width = 1552; + colors = 4; + filters = 0x1e4b4e1b; + load_raw = &CLASS canon_a5_load_raw; + } else if (!strcmp(model,"PowerShot Pro90 IS")) { + width = 1896; + colors = 4; + filters = 0xb4b4b4b4; + } else if (is_canon && raw_width == 2144) { + height = 1550; + width = 2088; + top_margin = 8; + left_margin = 4; + if (!strcmp(model,"PowerShot G1")) { + colors = 4; + filters = 0xb4b4b4b4; + } + } else if (is_canon && raw_width == 2224) { + height = 1448; + width = 2176; + top_margin = 6; + left_margin = 48; + } else if (is_canon && raw_width == 2376) { + height = 1720; + width = 2312; + top_margin = 6; + left_margin = 12; + } else if (is_canon && raw_width == 2672) { + height = 1960; + width = 2616; + top_margin = 6; + left_margin = 12; + } else if (is_canon && raw_width == 3152) { + height = 2056; + width = 3088; + top_margin = 12; + left_margin = 64; + if (unique_id == 0x80000170) + adobe_coeff ("Canon","EOS 300D"); + maximum = 0xfa0; + } else if (is_canon && raw_width == 3160) { + height = 2328; + width = 3112; + top_margin = 12; + left_margin = 44; + } else if (is_canon && raw_width == 3344) { + height = 2472; + width = 3288; + top_margin = 6; + left_margin = 4; + } else if (!strcmp(model,"EOS D2000C")) { + filters = 0x61616161; + black = curve[200]; + } else if (is_canon && raw_width == 3516) { + top_margin = 14; + left_margin = 42; + if (unique_id == 0x80000189) + adobe_coeff ("Canon","EOS 350D"); + goto canon_cr2; + } else if (is_canon && raw_width == 3596) { + top_margin = 12; + left_margin = 74; + goto canon_cr2; + } else if (is_canon && raw_width == 3948) { + top_margin = 18; + left_margin = 42; + height -= 2; + if (unique_id == 0x80000236) + adobe_coeff ("Canon","EOS 400D"); + goto canon_cr2; + } else if (is_canon && raw_width == 4476) { + top_margin = 34; + left_margin = 90; + maximum = 0xe6c; + goto canon_cr2; + } else if (is_canon && raw_width == 5108) { + top_margin = 13; + left_margin = 98; + maximum = 0xe80; +canon_cr2: + height -= top_margin; + width -= left_margin; + } else if (!strcmp(model,"D1")) { + cam_mul[0] *= 256/527.0; + cam_mul[2] *= 256/317.0; + } else if (!strcmp(model,"D1X")) { + width -= 4; + ymag = 2; + } else if (!strncmp(model,"D50",3) || !strncmp(model,"D70",3)) { + width--; + maximum = 0xf53; + } else if (!strcmp(model,"D80")) { + height -= 3; + width -= 4; + } else if (!strcmp(model,"D100")) { + if (tiff_compress == 34713 && load_raw == &CLASS nikon_load_raw) + raw_width = (width += 3) + 3; + maximum = 0xf44; + } else if (!strcmp(model,"D200")) { + left_margin = 1; + width -= 4; + maximum = 0xfbc; + filters = 0x94949494; + } else if (!strncmp(model,"D2H",3)) { + left_margin = 6; + width -= 14; + } else if (!strcmp(model,"D2X")) { + width -= 8; + } else if (fsize == 1581060) { + height = 963; + width = 1287; + raw_width = 1632; + load_raw = &CLASS nikon_e900_load_raw; + maximum = 0x3f4; + colors = 4; + filters = 0x1e1e1e1e; + simple_coeff(3); + pre_mul[0] = 1.2085; + pre_mul[1] = 1.0943; + pre_mul[3] = 1.1103; + } else if (fsize == 2465792) { + height = 1203; + width = 1616; + raw_width = 2048; + load_raw = &CLASS nikon_e900_load_raw; + maximum = 0x3dd; + colors = 4; + filters = 0x4b4b4b4b; + adobe_coeff ("NIKON","E950"); + } else if (fsize == 4771840) { + height = 1540; + width = 2064; + colors = 4; + filters = 0xe1e1e1e1; + load_raw = &CLASS nikon_load_raw; + if (!timestamp && nikon_e995()) + strcpy (model, "E995"); + if (strcmp(model,"E995")) { + filters = 0xb4b4b4b4; + simple_coeff(3); + pre_mul[0] = 1.196; + pre_mul[1] = 1.246; + pre_mul[2] = 1.018; + } + } else if (!strcmp(model,"E2100")) { + if (!timestamp && !nikon_e2100()) goto cp_e2500; + height = 1206; + width = 1616; + load_raw = &CLASS nikon_e2100_load_raw; + pre_mul[0] = 1.945; + pre_mul[2] = 1.040; + } else if (!strcmp(model,"E2500")) { +cp_e2500: + strcpy (model, "E2500"); + height = 1204; + width = 1616; + colors = 4; + filters = 0x4b4b4b4b; + } else if (fsize == 4775936) { + height = 1542; + width = 2064; + load_raw = &CLASS nikon_e2100_load_raw; + pre_mul[0] = 1.818; + pre_mul[2] = 1.618; + if (!timestamp) nikon_3700(); + if (model[0] == 'E' && atoi(model+1) < 3700) + filters = 0x49494949; + if (!strcmp(model,"Optio 33WR")) { + flip = 1; + filters = 0x16161616; + pre_mul[0] = 1.331; + pre_mul[2] = 1.820; + } + } else if (fsize == 5869568) { + height = 1710; + width = 2288; + filters = 0x16161616; + if (!timestamp && minolta_z2()) { + strcpy (make, "Minolta"); + strcpy (model,"DiMAGE Z2"); + } + if (make[0] == 'M') + load_raw = &CLASS nikon_e2100_load_raw; + } else if (!strcmp(model,"E4500")) { + height = 1708; + width = 2288; + colors = 4; + filters = 0xb4b4b4b4; + } else if (fsize == 7438336) { + height = 1924; + width = 2576; + colors = 4; + filters = 0xb4b4b4b4; + } else if (!strncmp(model,"R-D1",4)) { + tiff_compress = 34713; + load_raw = &CLASS nikon_load_raw; + } else if (!strcmp(model,"FinePix S5100") || + !strcmp(model,"FinePix S5500")) { + load_raw = &CLASS unpacked_load_raw; + maximum = 0x3e00; + } else if (!strncmp(model,"FinePix",7)) { + if (!strcmp(model+7,"S2Pro")) { + strcpy (model+7," S2Pro"); + height = 2144; + width = 2880; + flip = 6; + } else + maximum = 0x3e00; + top_margin = (raw_height - height)/2; + left_margin = (raw_width - width )/2; + if (fuji_secondary) + data_offset += use_secondary * ( strcmp(model+7," S3Pro") + ? (raw_width *= 2) : raw_height*raw_width*2 ); + fuji_width = width >> !fuji_layout; + width = (height >> fuji_layout) + fuji_width; + raw_height = height; + height = width - 1; + load_raw = &CLASS fuji_load_raw; + if (!(fuji_width & 1)) filters = 0x49494949; + } else if (!strcmp(model,"RD175")) { + height = 986; + width = 1534; + data_offset = 513; + filters = 0x61616161; + load_raw = &CLASS minolta_rd175_load_raw; + } else if (!strcmp(model,"KD-400Z")) { + height = 1712; + width = 2312; + raw_width = 2336; + goto konica_400z; + } else if (!strcmp(model,"KD-510Z")) { + goto konica_510z; + } else if (!strcasecmp(make,"MINOLTA")) { + load_raw = &CLASS unpacked_load_raw; + maximum = 0xf7d; + if (!strncmp(model,"DiMAGE A",8)) { + if (!strcmp(model,"DiMAGE A200")) + filters = 0x49494949; + load_raw = &CLASS packed_12_load_raw; + maximum = model[8] == '1' ? 0xf8b : 0xfff; + } else if (!strncmp(model,"ALPHA",5) || + !strncmp(model,"DYNAX",5) || + !strncmp(model,"MAXXUM",6)) { + sprintf (model+20, "DYNAX %-10s", model+6+(model[0]=='M')); + adobe_coeff (make, model+20); + load_raw = &CLASS packed_12_load_raw; + maximum = 0xffb; + } else if (!strncmp(model,"DiMAGE G",8)) { + if (model[8] == '4') { + height = 1716; + width = 2304; + } else if (model[8] == '5') { +konica_510z: + height = 1956; + width = 2607; + raw_width = 2624; + } else if (model[8] == '6') { + height = 2136; + width = 2848; + } + data_offset += 14; + filters = 0x61616161; +konica_400z: + load_raw = &CLASS unpacked_load_raw; + maximum = 0x3df; + order = 0x4d4d; + } + } else if (!strcmp(model,"*ist DS")) { + height -= 2; + } else if (!strcmp(model,"Optio S")) { + if (fsize == 3178560) { + height = 1540; + width = 2064; + load_raw = &CLASS eight_bit_load_raw; + cam_mul[0] *= 4; + cam_mul[2] *= 4; + pre_mul[0] = 1.391; + pre_mul[2] = 1.188; + } else { + height = 1544; + width = 2068; + raw_width = 3136; + load_raw = &CLASS packed_12_load_raw; + maximum = 0xf7c; + pre_mul[0] = 1.137; + pre_mul[2] = 1.453; + } + } else if (!strncmp(model,"Optio S4",8)) { + height = 1737; + width = 2324; + raw_width = 3520; + load_raw = &CLASS packed_12_load_raw; + maximum = 0xf7a; + pre_mul[0] = 1.980; + pre_mul[2] = 1.570; + } else if (!strcmp(model,"STV680 VGA")) { + height = 484; + width = 644; + load_raw = &CLASS eight_bit_load_raw; + flip = 2; + filters = 0x16161616; + black = 16; + pre_mul[0] = 1.097; + pre_mul[2] = 1.128; + } else if (!strcmp(model,"KAI-0340")) { + height = 477; + width = 640; + order = 0x4949; + data_offset = 3840; + load_raw = &CLASS unpacked_load_raw; + pre_mul[0] = 1.561; + pre_mul[2] = 2.454; + } else if (!strcmp(model,"531C")) { + height = 1200; + width = 1600; + load_raw = &CLASS unpacked_load_raw; + filters = 0x49494949; + pre_mul[1] = 1.218; + } else if (!strcmp(model,"F-145C")) { + height = 1040; + width = 1392; + load_raw = &CLASS eight_bit_load_raw; + } else if (!strcmp(model,"F-201C")) { + height = 1200; + width = 1600; + load_raw = &CLASS eight_bit_load_raw; + } else if (!strcmp(model,"F-510C")) { + height = 1958; + width = 2588; + load_raw = fsize < 7500000 ? + &CLASS eight_bit_load_raw : &CLASS unpacked_load_raw; + maximum = 0xfff0; + } else if (!strcmp(model,"F-810C")) { + height = 2469; + width = 3272; + load_raw = &CLASS unpacked_load_raw; + maximum = 0xfff0; + } else if (!strcmp(model,"XCD-SX910CR")) { + height = 1024; + width = 1375; + raw_width = 1376; + filters = 0x49494949; + maximum = 0x3ff; + load_raw = fsize < 2000000 ? + &CLASS eight_bit_load_raw : &CLASS unpacked_load_raw; + } else if (!strcmp(model,"2010")) { + height = 1207; + width = 1608; + order = 0x4949; + filters = 0x16161616; + data_offset = 3212; + maximum = 0x3ff; + load_raw = &CLASS unpacked_load_raw; + } else if (!strcmp(model,"A782")) { + height = 3000; + width = 2208; + filters = 0x61616161; + load_raw = fsize < 10000000 ? + &CLASS eight_bit_load_raw : &CLASS unpacked_load_raw; + maximum = 0xffc0; + } else if (!strcmp(model,"3320AF")) { + height = 1536; + raw_width = width = 2048; + filters = 0x61616161; + load_raw = &CLASS unpacked_load_raw; + maximum = 0x3ff; + pre_mul[0] = 1.717; + pre_mul[2] = 1.138; + fseek (ifp, 0x300000, SEEK_SET); + if ((order = guess_byte_order(0x10000)) == 0x4d4d) { + height -= (top_margin = 16); + width -= (left_margin = 28); + maximum = 0xf5c0; + strcpy (make, "ISG"); + model[0] = 0; + } + } else if (!strcmp(make,"Imacon")) { + sprintf (model, "Ixpress %d-Mp", height*width/1000000); + load_raw = &CLASS imacon_full_load_raw; + if (filters) { + if (left_margin & 1) filters = 0x61616161; + load_raw = &CLASS unpacked_load_raw; + } + maximum = 0xffff; + } else if (!strcmp(make,"Sinar")) { + if (!memcmp(head,"8BPS",4)) { + fseek (ifp, 14, SEEK_SET); + height = get4(); + width = get4(); + filters = 0x61616161; + data_offset = 68; + } + load_raw = &CLASS unpacked_load_raw; + maximum = 0x3fff; + } else if (!strcmp(make,"Leaf")) { + maximum = 0x3fff; + if (tiff_samples > 1) filters = 0; + if (tiff_samples > 1 || tile_length < raw_height) + load_raw = &CLASS leaf_hdr_load_raw; + if ((width | height) == 2048) { + if (tiff_samples == 1) { + filters = 1; + strcpy (cdesc, "RBTG"); + strcpy (model, "CatchLight"); + top_margin = 8; left_margin = 18; height = 2032; width = 2016; + } else { + strcpy (model, "DCB2"); + top_margin = 10; left_margin = 16; height = 2028; width = 2022; + } + } else if (width+height == 3144+2060) { + if (!model[0]) strcpy (model, "Cantare"); + if (width > height) { + top_margin = 6; left_margin = 32; height = 2048; width = 3072; + filters = 0x61616161; + } else { + left_margin = 6; top_margin = 32; width = 2048; height = 3072; + filters = 0x16161616; + } + if (!cam_mul[0] || model[0] == 'V') filters = 0; + } else if (width == 2116) { + strcpy (model, "Valeo 6"); + height -= 2 * (top_margin = 30); + width -= 2 * (left_margin = 55); + filters = 0x49494949; + } else if (width == 3171) { + strcpy (model, "Valeo 6"); + height -= 2 * (top_margin = 24); + width -= 2 * (left_margin = 24); + filters = 0x16161616; + } + } else if (!strcmp(make,"LEICA") || !strcmp(make,"Panasonic")) { + maximum = 0xfff0; + if (width == 2568) + adobe_coeff ("Panasonic","DMC-LC1"); + else if (width == 3304) { + maximum = 0xf94c; + width -= 16; + adobe_coeff ("Panasonic","DMC-FZ30"); + } else if (width == 3690) { + maximum = 0xf7f0; + height -= 3; + width = 3672; + left_margin = 3; + filters = 0x49494949; + adobe_coeff ("Panasonic","DMC-FZ50"); + } else if (width == 3770) { + height = 2760; + width = 3672; + top_margin = 15; + left_margin = 17; + adobe_coeff ("Panasonic","DMC-FZ50"); + } else if (width == 3880) { + maximum = 0xf7f0; + width -= 22; + left_margin = 6; + adobe_coeff ("Panasonic","DMC-LX1"); + } else if (width == 4290) { + height--; + width = 4248; + left_margin = 3; + filters = 0x49494949; + adobe_coeff ("Panasonic","DMC-LX2"); + } else if (width == 4330) { + height = 2400; + width = 4248; + top_margin = 15; + left_margin = 17; + adobe_coeff ("Panasonic","DMC-LX2"); + } + load_raw = &CLASS unpacked_load_raw; + } else if (!strcmp(model,"E-1")) { + filters = 0x61616161; + maximum = 0xfff0; + black = 1024; + } else if (!strcmp(model,"E-10")) { + maximum = 0xfff0; + black = 2048; + } else if (!strncmp(model,"E-20",4)) { + maximum = 0xffc0; + black = 2560; + } else if (!strcmp(model,"E-300") || + !strcmp(model,"E-500")) { + width -= 20; + maximum = 0xfc30; + } else if (!strcmp(model,"E-330")) { + width -= 30; + } else if (!strcmp(model,"C770UZ")) { + height = 1718; + width = 2304; + filters = 0x16161616; + load_raw = &CLASS nikon_e2100_load_raw; + } else if (!strcmp(make,"OLYMPUS")) { + load_raw = &CLASS olympus_cseries_load_raw; + if (!strcmp(model,"C5050Z") || + !strcmp(model,"C8080WZ")) + filters = 0x16161616; + if (!strcmp(model,"SP500UZ")) + filters = 0x49494949; + } else if (!strcmp(model,"N Digital")) { + height = 2047; + width = 3072; + filters = 0x61616161; + data_offset = 0x1a00; + load_raw = &CLASS packed_12_load_raw; + maximum = 0xf1e; + } else if (!strcmp(model,"DSC-F828")) { + width = 3288; + left_margin = 5; + data_offset = 862144; + load_raw = &CLASS sony_load_raw; + filters = 0x9c9c9c9c; + colors = 4; + strcpy (cdesc, "RGBE"); + } else if (!strcmp(model,"DSC-V3")) { + width = 3109; + left_margin = 59; + data_offset = 787392; + load_raw = &CLASS sony_load_raw; + } else if (!strcmp(make,"SONY") && raw_width == 3984) { + adobe_coeff ("SONY","DSC-R1"); + width = 3925; + order = 0x4d4d; + } else if (!strcmp(model,"DSLR-A100")) { + height--; + load_raw = &CLASS sony_arw_load_raw; + maximum = 0xfeb; + } else if (!strncmp(model,"P850",4)) { + maximum = 0xf7c; + } else if (!strcasecmp(make,"KODAK")) { + if (filters == UINT_MAX) filters = 0x61616161; + if (!strncmp(model,"NC2000",6)) { + width -= 4; + left_margin = 2; + } else if (!strcmp(model,"EOSDCS3B")) { + width -= 4; + left_margin = 2; + } else if (!strcmp(model,"EOSDCS1")) { + width -= 4; + left_margin = 2; + } else if (!strcmp(model,"DCS420")) { + width -= 4; + left_margin = 2; + } else if (!strcmp(model,"DCS460")) { + width -= 4; + left_margin = 2; + } else if (!strcmp(model,"DCS460A")) { + width -= 4; + left_margin = 2; + colors = 1; + filters = 0; + } else if (!strcmp(model,"DCS660M")) { + black = 214; + colors = 1; + filters = 0; + } else if (!strcmp(model,"DCS760M")) { + colors = 1; + filters = 0; + } + if (load_raw == &CLASS eight_bit_load_raw) + load_raw = &CLASS kodak_easy_load_raw; + if (strstr(model,"DC25")) { + strcpy (model, "DC25"); + data_offset = 15424; + } + if (!strncmp(model,"DC2",3)) { + height = 242; + if (fsize < 100000) { + raw_width = 256; width = 249; + } else { + raw_width = 512; width = 501; + } + data_offset += raw_width + 1; + colors = 4; + filters = 0x8d8d8d8d; + simple_coeff(1); + pre_mul[1] = 1.179; + pre_mul[2] = 1.209; + pre_mul[3] = 1.036; + load_raw = &CLASS kodak_easy_load_raw; + } else if (!strcmp(model,"40")) { + strcpy (model, "DC40"); + height = 512; + width = 768; + data_offset = 1152; + load_raw = &CLASS kodak_radc_load_raw; + } else if (strstr(model,"DC50")) { + strcpy (model, "DC50"); + height = 512; + width = 768; + data_offset = 19712; + load_raw = &CLASS kodak_radc_load_raw; + } else if (strstr(model,"DC120")) { + strcpy (model, "DC120"); + height = 976; + width = 848; + load_raw = tiff_compress == 7 ? + &CLASS kodak_jpeg_load_raw : &CLASS kodak_dc120_load_raw; + } + } else if (!strcmp(model,"Fotoman Pixtura")) { + height = 512; + width = 768; + data_offset = 3632; + load_raw = &CLASS kodak_radc_load_raw; + filters = 0x61616161; + simple_coeff(2); + } else if (!strcmp(make,"Rollei")) { + switch (raw_width) { + case 1316: + height = 1030; + width = 1300; + top_margin = 1; + left_margin = 6; + break; + case 2568: + height = 1960; + width = 2560; + top_margin = 2; + left_margin = 8; + } + filters = 0x16161616; + load_raw = &CLASS rollei_load_raw; + pre_mul[0] = 1.8; + pre_mul[2] = 1.3; + } else if (!strcmp(model,"PC-CAM 600")) { + height = 768; + data_offset = width = 1024; + filters = 0x49494949; + load_raw = &CLASS eight_bit_load_raw; + pre_mul[0] = 1.14; + pre_mul[2] = 2.73; + } else if (!strcmp(model,"QV-2000UX")) { + height = 1208; + width = 1632; + data_offset = width * 2; + load_raw = &CLASS eight_bit_load_raw; + } else if (fsize == 3217760) { + height = 1546; + width = 2070; + raw_width = 2080; + load_raw = &CLASS eight_bit_load_raw; + } else if (!strcmp(model,"QV-4000")) { + height = 1700; + width = 2260; + load_raw = &CLASS unpacked_load_raw; + maximum = 0xffff; + } else if (!strcmp(model,"QV-5700")) { + height = 1924; + width = 2576; + load_raw = &CLASS casio_qv5700_load_raw; + } else if (!strcmp(model,"QV-R51")) { + height = 1926; + width = 2576; + raw_width = 3904; + load_raw = &CLASS packed_12_load_raw; + pre_mul[0] = 1.340; + pre_mul[2] = 1.672; + } else if (!strcmp(model,"EX-S100")) { + height = 1544; + width = 2058; + raw_width = 3136; + load_raw = &CLASS packed_12_load_raw; + pre_mul[0] = 1.631; + pre_mul[2] = 1.106; + } else if (!strcmp(model,"EX-Z50")) { + height = 1931; + width = 2570; + raw_width = 3904; + load_raw = &CLASS packed_12_load_raw; + pre_mul[0] = 2.529; + pre_mul[2] = 1.185; + } else if (!strcmp(model,"EX-Z55")) { + height = 1960; + width = 2570; + raw_width = 3904; + load_raw = &CLASS packed_12_load_raw; + pre_mul[0] = 1.520; + pre_mul[2] = 1.316; + } else if (!strcmp(model,"EX-P505")) { + height = 1928; + width = 2568; + raw_width = 3852; + load_raw = &CLASS packed_12_load_raw; + pre_mul[0] = 2.07; + pre_mul[2] = 1.88; + } else if (fsize == 9313536) { /* EX-P600 or QV-R61 */ + height = 2142; + width = 2844; + raw_width = 4288; + load_raw = &CLASS packed_12_load_raw; + pre_mul[0] = 1.797; + pre_mul[2] = 1.219; + } else if (!strcmp(model,"EX-P700")) { + height = 2318; + width = 3082; + raw_width = 4672; + load_raw = &CLASS packed_12_load_raw; + pre_mul[0] = 1.758; + pre_mul[2] = 1.504; + } else if (!strcmp(make,"Nucore")) { + filters = 0x61616161; + load_raw = &CLASS unpacked_load_raw; + if (width == 2598) { + filters = 0x16161616; + load_raw = &CLASS nucore_load_raw; + flip = 2; + } + } + if (!model[0]) + sprintf (model, "%dx%d", width, height); + if (filters == UINT_MAX) filters = 0x94949494; + if (raw_color) adobe_coeff (make, model); + if (thumb_offset && !thumb_height) { + fseek (ifp, thumb_offset, SEEK_SET); + if (ljpeg_start (&jh, 1)) { + thumb_width = jh.wide; + thumb_height = jh.high; + } + } +dng_skip: + if (!load_raw || !height) is_raw = 0; +#ifdef NO_JPEG + if (load_raw == kodak_jpeg_load_raw) { + fprintf (stderr, "%s: You must link dcraw.c with libjpeg!!\n", ifname); + is_raw = 0; + } +#endif + if (flip == -1) flip = tiff_flip; + if (flip == -1) flip = 0; + if (!cdesc[0]) + strcpy (cdesc, colors == 3 ? "RGB":"GMCY"); + if (!raw_height) raw_height = height; + if (!raw_width ) raw_width = width; + if (filters && colors == 3) + for (i=0; i < 32; i+=4) { + if ((filters >> i & 15) == 9) + filters |= 2 << i; + if ((filters >> i & 15) == 6) + filters |= 8 << i; + } +} + +#ifndef NO_LCMS +void CLASS apply_profile (char *input, char *output) +{ + char *prof; + cmsHPROFILE hInProfile=NULL, hOutProfile=NULL; + cmsHTRANSFORM hTransform; + FILE *fp; + unsigned size; + + cmsErrorAction (LCMS_ERROR_SHOW); + if (strcmp (input, "embed")) + hInProfile = cmsOpenProfileFromFile (input, "r"); + else if (profile_length) { + prof = (char *) malloc (profile_length); + merror (prof, "apply_profile()"); + fseek (ifp, profile_offset, SEEK_SET); + fread (prof, 1, profile_length, ifp); + hInProfile = cmsOpenProfileFromMem (prof, profile_length); + free (prof); + } else + fprintf (stderr, "%s has no embedded profile.\n", ifname); + if (!hInProfile) return; + if (!output) + hOutProfile = cmsCreate_sRGBProfile(); + else if ((fp = fopen (output, "rb"))) { + fread (&size, 4, 1, fp); + fseek (fp, 0, SEEK_SET); + oprof = (unsigned *) malloc (size = ntohl(size)); + merror (oprof, "apply_profile()"); + fread (oprof, 1, size, fp); + fclose (fp); + if (!(hOutProfile = cmsOpenProfileFromMem (oprof, size))) { + free (oprof); + oprof = NULL; + } + } else + fprintf (stderr, "Cannot open %s!\n", output); + if (!hOutProfile) goto quit; + if (verbose) + fprintf (stderr, "Applying color profile...\n"); + hTransform = cmsCreateTransform (hInProfile, TYPE_RGBA_16, + hOutProfile, TYPE_RGBA_16, INTENT_PERCEPTUAL, 0); + cmsDoTransform (hTransform, image, image, width*height); + raw_color = 1; /* Don't use rgb_cam with a profile */ + cmsDeleteTransform (hTransform); + cmsCloseProfile (hOutProfile); +quit: + cmsCloseProfile (hInProfile); +} +#endif + +void CLASS convert_to_rgb() +{ + int mix_green, row, col, c, i, j, k; + ushort *img; + float out[3], out_cam[3][4]; + double num, inverse[3][3]; + static const double xyzd50_srgb[3][3] = + { { 0.436083, 0.385083, 0.143055 }, + { 0.222507, 0.716888, 0.060608 }, + { 0.013930, 0.097097, 0.714022 } }; + static const double rgb_rgb[3][3] = + { { 1,0,0 }, { 0,1,0 }, { 0,0,1 } }; + static const double adobe_rgb[3][3] = + { { 0.715146, 0.284856, 0.000000 }, + { 0.000000, 1.000000, 0.000000 }, + { 0.000000, 0.041166, 0.958839 } }; + static const double wide_rgb[3][3] = + { { 0.593087, 0.404710, 0.002206 }, + { 0.095413, 0.843149, 0.061439 }, + { 0.011621, 0.069091, 0.919288 } }; + static const double prophoto_rgb[3][3] = + { { 0.529317, 0.330092, 0.140588 }, + { 0.098368, 0.873465, 0.028169 }, + { 0.016879, 0.117663, 0.865457 } }; + static const double (*out_rgb[])[3] = + { rgb_rgb, adobe_rgb, wide_rgb, prophoto_rgb, xyz_rgb }; + static const char *name[] = + { "sRGB", "Adobe RGB (1998)", "WideGamut D65", "ProPhoto D65", "XYZ" }; + static const unsigned phead[] = + { 1024, 0, 0x2100000, 0x6d6e7472, 0x52474220, 0x58595a20, 0, 0, 0, + 0x61637370, 0, 0, 0x6e6f6e65, 0, 0, 0, 0, 0xf6d6, 0x10000, 0xd32d }; + unsigned pbody[] = + { 10, 0x63707274, 0, 36, /* cprt */ + 0x64657363, 0, 40, /* desc */ + 0x77747074, 0, 20, /* wtpt */ + 0x626b7074, 0, 20, /* bkpt */ + 0x72545243, 0, 14, /* rTRC */ + 0x67545243, 0, 14, /* gTRC */ + 0x62545243, 0, 14, /* bTRC */ + 0x7258595a, 0, 20, /* rXYZ */ + 0x6758595a, 0, 20, /* gXYZ */ + 0x6258595a, 0, 20 }; /* bXYZ */ + static const unsigned pwhite[] = { 0xf351, 0x10000, 0x116cc }; + unsigned pcurve[] = { 0x63757276, 0, 1, 0x1000000 }; + + memcpy (out_cam, rgb_cam, sizeof out_cam); + raw_color |= colors == 1 || document_mode || + output_color < 1 || output_color > 5; + if (!raw_color) { + oprof = calloc (phead[0], 1); + merror (oprof, "convert_to_rgb()"); + memcpy (oprof, phead, sizeof phead); + if (output_color == 5) oprof[4] = oprof[5]; + oprof[0] = 132 + 12*pbody[0]; + for (i=0; i < pbody[0]; i++) { + oprof[oprof[0]/4] = i ? (i > 1 ? 0x58595a20 : 0x64657363) : 0x74657874; + pbody[i*3+2] = oprof[0]; + oprof[0] += (pbody[i*3+3] + 3) & -4; + } + memcpy (oprof+32, pbody, sizeof pbody); + oprof[pbody[5]/4+2] = strlen(name[output_color-1]) + 1; + memcpy ((char *)oprof+pbody[8]+8, pwhite, sizeof pwhite); + if (output_bps == 8) +#ifdef SRGB_GAMMA + pcurve[3] = 0x2330000; +#else + pcurve[3] = 0x1f00000; +#endif + for (i=4; i < 7; i++) + memcpy ((char *)oprof+pbody[i*3+2], pcurve, sizeof pcurve); + pseudoinverse ((double (*)[3]) out_rgb[output_color-1], inverse, 3); + for (i=0; i < 3; i++) + for (j=0; j < 3; j++) { + for (num = k=0; k < 3; k++) + num += xyzd50_srgb[i][k] * inverse[j][k]; + oprof[pbody[j*3+23]/4+i+2] = num * 0x10000 + 0.5; + } + for (i=0; i < phead[0]/4; i++) + oprof[i] = htonl(oprof[i]); + strcpy ((char *)oprof+pbody[2]+8, "auto-generated by dcraw"); + strcpy ((char *)oprof+pbody[5]+12, name[output_color-1]); + for (i=0; i < 3; i++) + for (j=0; j < colors; j++) + for (out_cam[i][j] = k=0; k < 3; k++) + out_cam[i][j] += out_rgb[output_color-1][i][k] * rgb_cam[k][j]; + } + if (verbose) + fprintf (stderr, raw_color ? "Building histograms...\n" : + "Converting to %s colorspace...\n", name[output_color-1]); + + mix_green = rgb_cam[1][1] == rgb_cam[1][3]; + memset (histogram, 0, sizeof histogram); + for (img=image[0], row=0; row < height; row++) + for (col=0; col < width; col++, img+=4) { + if (!raw_color) { + out[0] = out[1] = out[2] = 0; + FORCC { + out[0] += out_cam[0][c] * img[c]; + out[1] += out_cam[1][c] * img[c]; + out[2] += out_cam[2][c] * img[c]; + } + FORC3 img[c] = CLIP((int) out[c]); + } + else if (document_mode) + img[0] = img[FC(row,col)]; + else if (mix_green) + img[1] = (img[1] + img[3]) >> 1; + FORCC histogram[c][img[c] >> 3]++; + } + if (colors == 4 && (output_color || mix_green)) colors = 3; + if (document_mode && filters) colors = 1; +} + +void CLASS fuji_rotate() +{ + int i, wide, high, row, col; + double step; + float r, c, fr, fc; + unsigned ur, uc; + ushort (*img)[4], (*pix)[4]; + + if (!fuji_width) return; + if (verbose) + fprintf (stderr, "Rotating image 45 degrees...\n"); + fuji_width = (fuji_width - 1 + shrink) >> shrink; + step = sqrt(0.5); + wide = fuji_width / step; + high = (height - fuji_width) / step; + img = (ushort (*)[4]) calloc (wide*high, sizeof *img); + merror (img, "fuji_rotate()"); + + for (row=0; row < high; row++) + for (col=0; col < wide; col++) { + ur = r = fuji_width + (row-col)*step; + uc = c = (row+col)*step; + if (ur > height-2 || uc > width-2) continue; + fr = r - ur; + fc = c - uc; + pix = image + ur*width + uc; + for (i=0; i < colors; i++) + img[row*wide+col][i] = + (pix[ 0][i]*(1-fc) + pix[ 1][i]*fc) * (1-fr) + + (pix[width][i]*(1-fc) + pix[width+1][i]*fc) * fr; + } + free (image); + width = wide; + height = high; + image = img; + fuji_width = 0; +} + +int CLASS flip_index (int row, int col) +{ + if (flip & 4) SWAP(row,col); + if (flip & 2) row = iheight - 1 - row; + if (flip & 1) col = iwidth - 1 - col; + return row * iwidth + col; +} + +void CLASS gamma_lut (uchar lut[0x10000]) +{ + int perc, c, val, total, i; + float white=0, r; + + perc = width * height * 0.01; /* 99th percentile white point */ + if (fuji_width) perc /= 2; + if (highlight) perc = 0; + FORCC { + for (val=0x2000, total=0; --val > 32; ) + if ((total += histogram[c][val]) > perc) break; + if (white < val) white = val; + } + white *= 8 / bright; + for (i=0; i < 0x10000; i++) { + r = i / white; + val = 256 * ( !use_gamma ? r : +#ifdef SRGB_GAMMA + r <= 0.00304 ? r*12.92 : pow(r,2.5/6)*1.055-0.055 ); +#else + r <= 0.018 ? r*4.5 : pow(r,0.45)*1.099-0.099 ); +#endif + if (val > 255) val = 255; + lut[i] = val; + } +} + +struct tiff_tag { + ushort tag, type; + int count; + union { short s0, s1; int i0; } val; +}; + +struct tiff_hdr { + ushort order, magic; + int ifd; + ushort pad, ntag; + struct tiff_tag tag[15]; + int nextifd; + ushort pad2, nexif; + struct tiff_tag exif[4]; + short bps[4]; + int rat[6]; + char make[64], model[72], soft[32], date[20]; +}; + +void CLASS tiff_set (ushort *ntag, + ushort tag, ushort type, int count, int val) +{ + struct tiff_tag *tt; + + tt = (struct tiff_tag *)(ntag+1) + (*ntag)++; + tt->tag = tag; + tt->type = type; + tt->count = count; + if (type == 3 && count == 1) + tt->val.s0 = val; + else tt->val.i0 = val; +} + +#define TOFF(ptr) ((char *)(&(ptr)) - (char *)(&th)) + +void CLASS write_ppm_tiff (FILE *ofp) +{ + struct tiff_hdr th; + struct tm *t; + uchar *ppm, lut[0x10000]; + ushort *ppm2; + int i, c, row, col, psize=0, soff, rstep, cstep; + + iheight = height; + iwidth = width; + if (flip & 4) { + SWAP(height,width); + SWAP(ymag,xmag); + } + ppm = (uchar *) calloc (width, colors*xmag*output_bps/8); + ppm2 = (ushort *) ppm; + merror (ppm, "write_ppm_tiff()"); + + memset (&th, 0, sizeof th); + th.order = htonl(0x4d4d4949) >> 16; + th.magic = 42; + th.ifd = 10; + tiff_set (&th.ntag, 256, 4, 1, xmag*width); + tiff_set (&th.ntag, 257, 4, 1, ymag*height); + tiff_set (&th.ntag, 258, 3, colors, output_bps); + if (colors > 2) + th.tag[th.ntag-1].val.i0 = TOFF(th.bps); + FORC4 th.bps[c] = output_bps; + tiff_set (&th.ntag, 259, 3, 1, 1); + tiff_set (&th.ntag, 262, 3, 1, 1 + (colors > 1)); + tiff_set (&th.ntag, 271, 2, 64, TOFF(th.make)); + tiff_set (&th.ntag, 272, 2, 72, TOFF(th.model)); + if (oprof) psize = ntohl(oprof[0]); + tiff_set (&th.ntag, 273, 4, 1, sizeof th + psize); + tiff_set (&th.ntag, 277, 3, 1, colors); + tiff_set (&th.ntag, 278, 4, 1, ymag*height); + tiff_set (&th.ntag, 279, 4, 1, ymag*height*xmag*width*colors*output_bps/8); + tiff_set (&th.ntag, 305, 2, 32, TOFF(th.soft)); + tiff_set (&th.ntag, 306, 2, 20, TOFF(th.date)); + tiff_set (&th.ntag, 34665, 4, 1, TOFF(th.nexif)); + if (psize) tiff_set (&th.ntag, 34675, 7, psize, sizeof th); + tiff_set (&th.nexif, 33434, 5, 1, TOFF(th.rat[0])); + tiff_set (&th.nexif, 33437, 5, 1, TOFF(th.rat[2])); + tiff_set (&th.nexif, 34855, 3, 1, iso_speed); + tiff_set (&th.nexif, 37386, 5, 1, TOFF(th.rat[4])); + for (i=0; i < 6; i++) th.rat[i] = 1000000; + th.rat[0] *= shutter; + th.rat[2] *= aperture; + th.rat[4] *= focal_len; + strncpy (th.make, make, 64); + strncpy (th.model, model, 72); + strcpy (th.soft, "dcraw v"VERSION); + t = gmtime (×tamp); + sprintf (th.date, "%04d:%02d:%02d %02d:%02d:%02d", + t->tm_year+1900,t->tm_mon+1,t->tm_mday,t->tm_hour,t->tm_min,t->tm_sec); + + if (output_tiff) { + fwrite (&th, sizeof th, 1, ofp); + if (psize) + fwrite (oprof, psize, 1, ofp); + } else if (colors > 3) + fprintf (ofp, + "P7\nWIDTH %d\nHEIGHT %d\nDEPTH %d\nMAXVAL %d\nTUPLTYPE %s\nENDHDR\n", + xmag*width, ymag*height, colors, (1 << output_bps)-1, cdesc); + else + fprintf (ofp, "P%d\n%d %d\n%d\n", + colors/2+5, xmag*width, ymag*height, (1 << output_bps)-1); + + if (output_bps == 8) + gamma_lut (lut); + soff = flip_index (0, 0); + cstep = flip_index (0, 1) - soff; + rstep = flip_index (1, 0) - flip_index (0, width); + for (row=0; row < height; row++, soff += rstep) { + for (col=0; col < width; col++, soff += cstep) + FORCC for (i=0; i < xmag; i++) + if (output_bps == 8) + ppm [(col*xmag+i)*colors+c] = lut[image[soff][c]]; + else ppm2[(col*xmag+i)*colors+c] = image[soff][c]; + if (output_bps == 16 && !output_tiff && th.order == 0x4949) + swab (ppm2, ppm2, xmag*width*colors*2); + for (i=0; i < ymag; i++) + fwrite (ppm, colors*output_bps/8, xmag*width, ofp); + } + free (ppm); +} + +int CLASS main (int argc, char **argv) +{ + int arg, status=0, user_flip=-1, user_black=-1, user_qual=-1; + int timestamp_only=0, thumbnail_only=0, identify_only=0, write_to_stdout=0; + int half_size=0, use_fuji_rotate=1, quality, i, c; + char opt, *ofname, *sp, *cp; + const char *write_ext; + struct utimbuf ut; + FILE *ofp = stdout; +#ifndef NO_LCMS + char *cam_profile = NULL, *out_profile = NULL; +#endif + +#ifndef LOCALTIME + putenv ("TZ=UTC"); +#endif + if (argc == 1) + { + fprintf (stderr, + "\nRaw Photo Decoder \"dcraw\" v"VERSION + "\nby Dave Coffin, dcoffin a cybercom o net" + "\n\nUsage: %s [options] file1 file2 ...\n" + "\nValid options:" + "\n-v Print verbose messages" + "\n-c Write image data to standard output" + "\n-e Extract embedded thumbnail image" + "\n-i Identify files without decoding them" + "\n-z Change file dates to camera timestamp" + "\n-a Use automatic white balance" + "\n-w Use camera white balance, if possible" + "\n-r Set raw white balance (four values required)" + "\n-b Adjust brightness (default = 1.0)" + "\n-k Set black point" + "\n-H [0-9] Highlight mode (0=clip, 1=no clip, 2+=recover)" + "\n-t [0-7] Flip image (0=none, 3=180, 5=90CCW, 6=90CW)" + "\n-o [0-5] Output colorspace (raw,sRGB,Adobe,Wide,ProPhoto,XYZ)" +#ifndef NO_LCMS + "\n-o Apply output ICC profile from file" + "\n-p Apply camera ICC profile from file or \"embed\"" +#endif + "\n-d Document Mode (no color, no interpolation)" + "\n-D Document Mode without scaling (totally raw)" + "\n-q [0-3] Set the interpolation quality" + "\n-h Half-size color image (twice as fast as \"-q 0\")" + "\n-f Interpolate RGGB as four colors" + "\n-B Apply bilateral filter to reduce noise" + "\n-j Show Fuji Super CCD images tilted 45 degrees" + "\n-s Use secondary pixels (Fuji Super CCD SR only)" + "\n-4 Write 16-bit linear instead of 8-bit with gamma" + "\n-T Write TIFF instead of PPM" + "\n\n", argv[0]); + return 1; + } + + argv[argc] = ""; + for (arg=1; argv[arg][0] == '-'; ) { + opt = argv[arg++][1]; + if ((cp = strchr (sp="BbrktqH", opt))) + for (i=0; i < "2141111"[cp-sp]-'0'; i++) + if (!isdigit(argv[arg+i][0])) { + fprintf (stderr, "Non-numeric argument to \"-%c\"\n", opt); + return 1; + } + switch (opt) + { + case 'B': sigma_d = atof(argv[arg++]); + sigma_r = atof(argv[arg++]); break; + case 'b': bright = atof(argv[arg++]); break; + case 'r': + FORC4 user_mul[c] = atof(argv[arg++]); break; + case 'k': user_black = atoi(argv[arg++]); break; + case 't': user_flip = atoi(argv[arg++]); break; + case 'q': user_qual = atoi(argv[arg++]); break; + case 'H': highlight = atoi(argv[arg++]); break; + case 'o': + if (isdigit(argv[arg][0]) && !argv[arg][1]) + output_color = atoi(argv[arg++]); +#ifndef NO_LCMS + else out_profile = argv[arg++]; + break; + case 'p': cam_profile = argv[arg++]; +#endif + break; + case 'z': timestamp_only = 1; break; + case 'e': thumbnail_only = 1; break; + case 'i': identify_only = 1; break; + case 'c': write_to_stdout = 1; break; + case 'v': verbose = 1; break; + case 'h': half_size = 1; /* "-h" implies "-f" */ + case 'f': four_color_rgb = 1; break; + case 'd': document_mode = 1; break; + case 'D': document_mode = 2; break; + case 'a': use_auto_wb = 1; break; + case 'w': use_camera_wb = 1; break; + case 'j': use_fuji_rotate = 0; break; + case 's': use_secondary = 1; break; + case 'm': output_color = 0; break; + case 'T': output_tiff = 1; break; + case '4': output_bps = 16; break; + default: + fprintf (stderr, "Unknown option \"-%c\".\n", opt); + return 1; + } + } + if (arg == argc) { + fprintf (stderr, "No files to process.\n"); + return 1; + } + if (write_to_stdout) { + if (isatty(1)) { + fprintf (stderr, "Will not write an image to the terminal!\n"); + return 1; + } +#if defined(WIN32) || defined(DJGPP) || defined(__CYGWIN__) + if (setmode(1,O_BINARY) < 0) { + perror("setmode()"); + return 1; + } +#endif + } + for ( ; arg < argc; arg++) { + status = 1; + image = NULL; + oprof = NULL; + if (setjmp (failure)) { + if (fileno(ifp) > 2) fclose(ifp); + if (fileno(ofp) > 2) fclose(ofp); + if (image) free (image); + status = 1; + continue; + } + ifname = argv[arg]; + if (!(ifp = fopen (ifname, "rb"))) { + perror (ifname); + continue; + } + status = (identify(),!is_raw); + if (timestamp_only) { + if ((status = !timestamp)) + fprintf (stderr, "%s has no timestamp.\n", ifname); + else if (identify_only) + printf ("%10ld%10d %s\n", (long) timestamp, shot_order, ifname); + else { + if (verbose) + fprintf (stderr, "%s time set to %d.\n", ifname, (int) timestamp); + ut.actime = ut.modtime = timestamp; + utime (ifname, &ut); + } + goto next; + } + write_fun = &CLASS write_ppm_tiff; + if (thumbnail_only) { + if ((status = !thumb_offset)) { + fprintf (stderr, "%s has no thumbnail.\n", ifname); + goto next; + } else if (thumb_load_raw) { + load_raw = thumb_load_raw; + data_offset = thumb_offset; + height = thumb_height; + width = thumb_width; + filters = 0; + } else { + fseek (ifp, thumb_offset, SEEK_SET); + write_fun = write_thumb; + goto thumbnail; + } + } + if (load_raw == &CLASS kodak_ycbcr_load_raw) { + height += height & 1; + width += width & 1; + } + if (identify_only && verbose && make[0]) { + printf ("\nFilename: %s\n", ifname); + printf ("Timestamp: %s", ctime(×tamp)); + printf ("Camera: %s %s\n", make, model); + printf ("ISO speed: %d\n", (int) iso_speed); + printf ("Shutter: "); + if (shutter > 0 && shutter < 1) + shutter = (printf ("1/"), 1 / shutter); + printf ("%0.1f sec\n", shutter); + printf ("Aperture: f/%0.1f\n", aperture); + printf ("Focal Length: %0.1f mm\n", focal_len); + printf ("Secondary pixels: %s\n", fuji_secondary ? "yes":"no"); + printf ("Embedded ICC profile: %s\n", profile_length ? "yes":"no"); + printf ("Decodable with dcraw: %s\n", is_raw ? "yes":"no"); + if (thumb_offset) + printf ("Thumb size: %4d x %d\n", thumb_width, thumb_height); + printf ("Full size: %4d x %d\n", raw_width, raw_height); + } else if (!is_raw) + fprintf (stderr, "Cannot decode %s\n", ifname); + if (!is_raw) goto next; + if (user_flip >= 0) + flip = user_flip; + switch ((flip+3600) % 360) { + case 270: flip = 5; break; + case 180: flip = 3; break; + case 90: flip = 6; + } + shrink = half_size && filters; + iheight = (height + shrink) >> shrink; + iwidth = (width + shrink) >> shrink; + if (identify_only) { + if (verbose) { + if (fuji_width && use_fuji_rotate) { + fuji_width = (fuji_width - 1 + shrink) >> shrink; + iwidth = fuji_width / sqrt(0.5); + iheight = (iheight - fuji_width) / sqrt(0.5); + } + iheight *= ymag; + iwidth *= xmag; + if (flip & 4) + SWAP(iheight,iwidth); + printf ("Image size: %4d x %d\n", width, height); + printf ("Output size: %4d x %d\n", iwidth, iheight); + printf ("Raw colors: %d", colors); + if (filters) { + printf ("\nFilter pattern: "); + if (!cdesc[3]) cdesc[3] = 'G'; + for (i=0; i < 16; i++) + putchar (cdesc[fc(i >> 1,i & 1)]); + } + printf ("\nDaylight multipliers:"); + FORCC printf (" %f", pre_mul[c]); + if (cam_mul[0] > 0) { + printf ("\nCamera multipliers:"); + FORC4 printf (" %f", cam_mul[c]); + } + putchar ('\n'); + } else + printf ("%s is a %s %s image.\n", ifname, make, model); +next: + fclose(ifp); + continue; + } + image = (ushort (*)[4]) + calloc (iheight*iwidth*sizeof *image + meta_length, 1); + merror (image, "main()"); + meta_data = (char *) (image + iheight*iwidth); + if (verbose) + fprintf (stderr, + "Loading %s %s image from %s...\n", make, model, ifname); + fseek (ifp, data_offset, SEEK_SET); + (*load_raw)(); + bad_pixels(); + height = iheight; + width = iwidth; + quality = 2 + !fuji_width; + if (user_qual >= 0) quality = user_qual; + if (user_black >= 0) black = user_black; +#ifdef COLORCHECK + colorcheck(); +#endif + if (is_foveon && !document_mode) foveon_interpolate(); + if (!is_foveon && document_mode < 2) scale_colors(); + if (shrink) filters = 0; + cam_to_cielab (NULL,NULL); + if (filters && !document_mode) { + if (quality == 0) + lin_interpolate(); + else if (quality < 3 || colors > 3) + vng_interpolate(); + else ahd_interpolate(); + } + if (sigma_d > 0 && sigma_r > 0) bilateral_filter(); + if (!is_foveon && highlight > 1) recover_highlights(); + if (use_fuji_rotate) fuji_rotate(); +#ifndef NO_LCMS + if (cam_profile) apply_profile (cam_profile, out_profile); +#endif + convert_to_rgb(); +thumbnail: + if (write_fun == &CLASS jpeg_thumb) + write_ext = ".jpg"; + else if (output_tiff && write_fun == &CLASS write_ppm_tiff) + write_ext = ".tiff"; + else + write_ext = ".pgm\0.ppm\0.ppm\0.pam" + colors*5-5; + ofname = (char *) malloc (strlen(ifname) + 16); + merror (ofname, "main()"); + if (write_to_stdout) + strcpy (ofname, "standard output"); + else { + strcpy (ofname, ifname); + if ((cp = strrchr (ofname, '.'))) *cp = 0; + if (thumbnail_only) + strcat (ofname, ".thumb"); + strcat (ofname, write_ext); + ofp = fopen (ofname, "wb"); + if (!ofp) { + status = 1; + perror(ofname); + goto cleanup; + } + } + if (verbose) + fprintf (stderr, "Writing data to %s ...\n", ofname); + (*write_fun)(ofp); + fclose(ifp); + if (ofp != stdout) fclose(ofp); +cleanup: + if (oprof) free(oprof); + free (ofname); + free (image); + } + return status; +} diff --git a/dcraw.changes b/dcraw.changes new file mode 100644 index 0000000..483fea9 --- /dev/null +++ b/dcraw.changes @@ -0,0 +1,104 @@ +------------------------------------------------------------------- +Mon Oct 16 22:47:15 CEST 2006 - postadal@suse.cz + +- updated to RCS 1.353 + * added support for Pentax K100D, Nikon D200, D80, E3200, + Canon EOS 400D, Panasonic DMC-LX2, DMC-FZ50 + * correctly display long exposure times for CRW files + * added color matrices from Adobe DNG Converter 3.5 + * display the focal length of Canon CRW images + +------------------------------------------------------------------- +Thu Aug 17 17:35:46 CEST 2006 - postadal@suse.cz + +- updated to RCS 1.340 + * improved support for the Olympus E-330 + +------------------------------------------------------------------- +Wed Aug 2 12:35:19 CEST 2006 - postadal@suse.cz + +- updated to RCS 1.338 + * added "-H 2" option for rudimentary highlight recovery + * new "-T" option provides TIFF output with metadata and ICC profile + * replaced "-r" and "-l" options with raw white balance + * added support for Imacon Ixpress 22-Mp, Leaf Aptus 75, Leica D-Lux 2, + Sony DSLR-A100, Minolta DiMAGE G530, Sinar 4080x4080, Samsung GX-1S + * fixed camera WB for Canon EOS 10D, 300D, and clones + * added camera WB for the Kodak DCS Pro SLR models, Imacon Ixpress, + Kodak P850/P880 + +------------------------------------------------------------------- +Sun Mar 26 15:56:10 CEST 2006 - postadal@suse.cz + +- updated to RCS 1.319 + * new color matrix for the Nikon E700, E800, and E950 + * new "-D" option to output unscaled raw pixels (totally raw) + * fixed defective pixels and columns in Phase One images + +------------------------------------------------------------------- +Thu Feb 9 22:12:25 CET 2006 - dmueller@suse.de + +- update to RCS 1.315 (#112733) + +------------------------------------------------------------------- +Wed Feb 8 02:36:13 CET 2006 - dmueller@suse.de + +- update to RCS 1.314: + Updated color matrices based on Adobe DNG Converter 3.3. + Added Kodak ProPhoto D65 as an output option. + +------------------------------------------------------------------- +Thu Jan 26 12:29:16 CET 2006 - sbrabec@suse.cz + +- Updated to dcraw v8, RCS 1.313. +- Added simple update_dcraw script. + +------------------------------------------------------------------- +Wed Jan 25 21:35:24 CET 2006 - mls@suse.de + +- converted neededforbuild to BuildRequires + +------------------------------------------------------------------- +Wed Jan 11 15:56:34 CET 2006 - adrian@suse.de + +- add stack protector compiler flag + +------------------------------------------------------------------- +Thu Oct 13 13:48:05 CEST 2005 - postadal@suse.cz + +- update to RCS 1.290 + * added as default new interpolation algorithm AHD + (Adaptive homogeneity-directed demosaicing algorithm) + * new Bilateral filtering to remove color noises in CIELAB space +- update dcraw.1, fujiturn, dcparse (parse.c) +- removed crwfixdates (fixdates.c) superseded by "dcraw -z" + +------------------------------------------------------------------- +Tue Jun 7 12:00:09 CEST 2005 - adrian@suse.de + +- update to RCS 1.263 +- compile with -fno-strict-aliasing to avoid compiler errors + +------------------------------------------------------------------- +Fri Jun 3 12:46:05 CEST 2005 - postadal@suse.cz + +- Updated to current version on maintainers website. + RCS version 1.262. + +------------------------------------------------------------------- +Fri Feb 4 15:43:01 CET 2005 - meissner@suse.de + +- Updated to current version on maintainers website. + RCS version 1.234. + +------------------------------------------------------------------- +Thu Aug 26 14:05:18 CEST 2004 - sbrabec@suse.cz + +- Updated to dcraw RCS version 1.200. +- Added more tools and documentation. + +------------------------------------------------------------------- +Wed Jun 16 10:48:17 CEST 2004 - adrian@suse.de + +- initial package of version 1.194 + diff --git a/dcraw.spec b/dcraw.spec new file mode 100644 index 0000000..32ec207 --- /dev/null +++ b/dcraw.spec @@ -0,0 +1,138 @@ +# +# spec file for package dcraw (Version 1.353) +# +# Copyright (c) 2006 SUSE LINUX Products GmbH, Nuernberg, Germany. +# This file and all modifications and additions to the pristine +# package are under the same license as the package itself. +# +# Please submit bugfixes or comments via http://bugs.opensuse.org/ +# + +# norootforbuild + +Name: dcraw +BuildRequires: libjpeg-devel liblcms-devel +Version: 1.353 +Release: 1 +License: distributable, Other License(s), see package +Group: Productivity/Graphics/Convertors +URL: http://www.cybercom.net/~dcoffin/dcraw/ +Summary: Raw Digital Photo Decoding +# NOTE: call "sh update_dcraw" to update to latest version. +Source: http://www.cybercom.net/~dcoffin/dcraw/dcraw.c +Source1: http://www.cybercom.net/~dcoffin/dcraw/dcraw.1 +Source2: README +Source3: README.SuSE +Source4: badpixels +Source5: http://www.cybercom.net/~dcoffin/dcraw/dcwrap +Source7: http://www.cybercom.net/~dcoffin/dcraw/fujiturn.c +Source8: http://www.cybercom.net/~dcoffin/dcraw/parse.c +Source9: update_dcraw +Autoreqprov: on +BuildRoot: %{_tmppath}/%{name}-%{version}-build + +%description +Command line tools for raw digital photo decoding and processing. + + + +Authors: +-------- + Dave Coffin + +%prep +%setup -q -n . -D -T 0 + cp -a %SOURCE0 %SOURCE1 %SOURCE2 %SOURCE3 %SOURCE4 %SOURCE5 %SOURCE7 %SOURCE8 . + mv badpixels .badpixels + chmod +x dcwrap + +%build + export CFLAGS="$RPM_OPT_FLAGS" +%if %suse_version > 1000 + export CFLAGS="$CFLAGS -fstack-protector-all" +%endif + for file in *.c ; do + LDFLAGS= + if test $file = dcraw.c ; then + LDFLAGS="-lm -ljpeg -llcms" + fi + gcc $CFLAGS -fno-strict-aliasing -o ${file%.c} $file $LDFLAGS + done + +%install + mkdir -p $RPM_BUILD_ROOT/%_bindir + mkdir -p $RPM_BUILD_ROOT%_mandir/man1 + install dcraw $RPM_BUILD_ROOT/%_bindir/ + install -m 0644 dcraw.1 $RPM_BUILD_ROOT/%_mandir/man1/ + install parse $RPM_BUILD_ROOT/%_bindir/dcparse + install fujiturn $RPM_BUILD_ROOT/%_bindir/ + +%clean +rm -rf $RPM_BUILD_ROOT + +%files +%defattr(-,root,root) +%doc README README.SuSE .badpixels dcwrap +%_bindir/* +%doc %_mandir/man*/* + +%changelog -n dcraw +* Mon Oct 16 2006 - postadal@suse.cz +- updated to RCS 1.353 + * added support for Pentax K100D, Nikon D200, D80, E3200, + Canon EOS 400D, Panasonic DMC-LX2, DMC-FZ50 + * correctly display long exposure times for CRW files + * added color matrices from Adobe DNG Converter 3.5 + * display the focal length of Canon CRW images +* Thu Aug 17 2006 - postadal@suse.cz +- updated to RCS 1.340 + * improved support for the Olympus E-330 +* Wed Aug 02 2006 - postadal@suse.cz +- updated to RCS 1.338 + * added "-H 2" option for rudimentary highlight recovery + * new "-T" option provides TIFF output with metadata and ICC profile + * replaced "-r" and "-l" options with raw white balance + * added support for Imacon Ixpress 22-Mp, Leaf Aptus 75, Leica D-Lux 2, + Sony DSLR-A100, Minolta DiMAGE G530, Sinar 4080x4080, Samsung GX-1S + * fixed camera WB for Canon EOS 10D, 300D, and clones + * added camera WB for the Kodak DCS Pro SLR models, Imacon Ixpress, + Kodak P850/P880 +* Sun Mar 26 2006 - postadal@suse.cz +- updated to RCS 1.319 + * new color matrix for the Nikon E700, E800, and E950 + * new "-D" option to output unscaled raw pixels (totally raw) + * fixed defective pixels and columns in Phase One images +* Thu Feb 09 2006 - dmueller@suse.de +- update to RCS 1.315 (#112733) +* Wed Feb 08 2006 - dmueller@suse.de +- update to RCS 1.314: + Updated color matrices based on Adobe DNG Converter 3.3. + Added Kodak ProPhoto D65 as an output option. +* Thu Jan 26 2006 - sbrabec@suse.cz +- Updated to dcraw v8, RCS 1.313. +- Added simple update_dcraw script. +* Wed Jan 25 2006 - mls@suse.de +- converted neededforbuild to BuildRequires +* Wed Jan 11 2006 - adrian@suse.de +- add stack protector compiler flag +* Thu Oct 13 2005 - postadal@suse.cz +- update to RCS 1.290 + * added as default new interpolation algorithm AHD + (Adaptive homogeneity-directed demosaicing algorithm) + * new Bilateral filtering to remove color noises in CIELAB space +- update dcraw.1, fujiturn, dcparse (parse.c) +- removed crwfixdates (fixdates.c) superseded by "dcraw -z" +* Tue Jun 07 2005 - adrian@suse.de +- update to RCS 1.263 +- compile with -fno-strict-aliasing to avoid compiler errors +* Fri Jun 03 2005 - postadal@suse.cz +- Updated to current version on maintainers website. + RCS version 1.262. +* Fri Feb 04 2005 - meissner@suse.de +- Updated to current version on maintainers website. + RCS version 1.234. +* Thu Aug 26 2004 - sbrabec@suse.cz +- Updated to dcraw RCS version 1.200. +- Added more tools and documentation. +* Wed Jun 16 2004 - adrian@suse.de +- initial package of version 1.194 diff --git a/dcwrap b/dcwrap new file mode 100644 index 0000000..7dbec7f --- /dev/null +++ b/dcwrap @@ -0,0 +1,27 @@ +#!/bin/sh + +# Rough equivalents to XV's Sharpen algorithm + +cat > sharpen50.pgm << xx +P2 3 3 18 +8 8 8 8 26 8 8 8 8 +xx +cat > sharpen66.pgm << xx +P2 3 3 18 +7 7 7 7 34 7 7 7 7 +xx +cat > sharpen75.pgm << xx +P2 3 3 6 +2 2 2 2 14 2 2 2 2 +xx +cat > sharpen90.pgm << xx +P2 3 3 2 +0 0 0 0 10 0 0 0 0 +xx + +for x in `ls *.crw | cut -d. -f1` +do + dcraw -f -c $x.crw | pnmconvol sharpen66.pgm \ + | cjpeg -quality 90 > $x.jpeg + touch -r $x.crw $x.jpeg +done diff --git a/fujiturn.c b/fujiturn.c new file mode 100644 index 0000000..abad34a --- /dev/null +++ b/fujiturn.c @@ -0,0 +1,102 @@ +/* + fujiturn.c by Dave Coffin + + UNIX filter to correct the 45-degree rotation in images from + Fuji digital cameras. Compile with -D_16BIT to rotate 48-bit + PPM images. Sample usage: + + dcraw -c -j dscf0000.raf | fujiturn | pnmscale 0.70710678 > dscf0000.ppm + + $Revision: 1.6 $ + $Date: 2005/04/29 16:35:42 $ + + */ + +#include +#include +#include + +#ifdef _16BIT +typedef unsigned short value; +#else +typedef unsigned char value; +#define ntohs(x) (x) +#define htons(x) (x) +#endif + +void merror (void *ptr, char *what) +{ + if (ptr) return; + fprintf (stderr, "Not enough memory for %s\n", what); + exit(1); +} + +int main() +{ + FILE *ifp, *ofp; + value (*in)[3], (*mid)[3], (*pix)[3], (*out)[3]; + char nl; + int maxval, i, j, iwide, ihigh, owide, ohigh; + unsigned irow, icol, orow, ocol; + +#if defined(WIN32) || defined(DJGPP) + if (setmode(0,O_BINARY) < 0) perror("setmode(0)"); + if (setmode(1,O_BINARY) < 0) perror("setmode(1)"); +#endif + ifp = stdin; + ofp = stdout; + if (fscanf (ifp, "P6 %d %d %d%c", &iwide, &ihigh, &maxval, &nl) != 4 + || abs(iwide - ihigh) > 1) { + fprintf (stderr, "Input is not a Fuji image processed by dcraw.\n"); + exit(1); + } + i = (maxval > 255) ? 16 : 8; + j = 8 * sizeof (value); + if (i != j) { + fprintf (stderr, "Input is %d-bit, fujiturn is %d-bit\n", i, j); + exit(1); + } + in = calloc (iwide, sizeof *in); + merror (in, "input array"); + fread (in, iwide, sizeof *in, ifp); + for (i = 0; i < iwide; i++) + if (in[i][0] || in[i][1] || in[i][2]) break; + ohigh = (iwide - i) * 2 - 4; + for (i = iwide; --i;) + if (in[i][0] || in[i][1] || in[i][2]) break; + owide = i; + mid = calloc (ohigh * owide, sizeof *mid); + merror (mid, "middle array"); + for (irow = 0; irow < ihigh; irow++) { + for (icol = 0; icol < iwide; icol++) { + orow = irow + icol - owide + 5; + ocol = (icol - irow + owide - 1)/2; + if (orow < ohigh && ocol < owide) + for (i = 0; i < 3; i++) + mid[orow*owide+ocol][i] = ntohs(in[icol][i]); + } + fread (in, iwide, sizeof *in, ifp); + } + free(in); + out = calloc (2*owide, sizeof *out); + merror (out, "output array"); + fprintf (ofp, "P6\n%d %d\n%d\n", owide*2, ohigh, maxval); + for (orow = 0; orow < ohigh; orow++) { + for (ocol = 0; ocol < owide*2; ocol++) { + pix = mid + orow*owide + ocol/2; + if ((orow+ocol) & 1) { + if (orow-1 < ohigh-2 && ocol-1 < owide*2-2) + for (i = 0; i < 3; i++) + out[ocol][i] = htons ( + ( pix[-owide][i] + pix[0-(orow&1)][i] + + pix[ owide][i] + pix[1-(orow&1)][i] ) >> 2); + } else + for (i = 0; i < 3; i++) + out[ocol][i] = htons(pix[0][i]); + } + fwrite (out, 2*owide, 3*sizeof (value), ofp); + } + free(mid); + free(out); + return 0; +} diff --git a/parse.c b/parse.c new file mode 100644 index 0000000..0bea141 --- /dev/null +++ b/parse.c @@ -0,0 +1,991 @@ +/* + Raw Photo Parser + Copyright 2004-2006 by Dave Coffin, dcoffin a cybercom o net + + This program displays raw metadata for all raw photo formats. + It is free for all uses. + + $Revision: 1.62 $ + $Date: 2006/08/18 02:51:53 $ + */ + +#include +#include +#include +#include + +#ifdef WIN32 +#include +typedef __int64 INT64; +#else +#include +typedef long long INT64; +#endif + +/* + TIFF and CIFF data blocks can be quite large. + Display only the first DLEN bytes. + */ +#ifndef DLEN +#define DLEN 768 +#endif + +#define ushort UshORt +typedef unsigned char uchar; +typedef unsigned short ushort; + +FILE *ifp; +short order; +char *fname, make[128], model[128], model2[128]; +int is_dng; + +ushort sget2 (uchar *s) +{ + if (order == 0x4949) /* "II" means little-endian */ + return s[0] | s[1] << 8; + else /* "MM" means big-endian */ + return s[0] << 8 | s[1]; +} +#define sget2(s) sget2((uchar *)s) + +ushort get2() +{ + uchar str[2] = { 0xff,0xff }; + fread (str, 1, 2, ifp); + return sget2(str); +} + +int sget4 (uchar *s) +{ + if (order == 0x4949) + return s[0] | s[1] << 8 | s[2] << 16 | s[3] << 24; + else + return s[0] << 24 | s[1] << 16 | s[2] << 8 | s[3]; +} +#define sget4(s) sget4((uchar *)s) + +int get4() +{ + uchar str[4] = { 0xff,0xff,0xff,0xff }; + fread (str, 1, 4, ifp); + return sget4(str); +} + +float int_to_float (int i) +{ + union { int i; float f; } u; + u.i = i; + return u.f; +} + +double get_double() +{ + union { char c[8]; double d; } u; + int i, rev; + + rev = 7 * ((order == 0x4949) == (ntohs(0x1234) == 0x1234)); + for (i=0; i < 8; i++) + u.c[i ^ rev] = fgetc(ifp); + return u.d; +} + +void tiff_dump(int base, int tag, int type, int count, int level) +{ + int save, j, num, den; + uchar c; + int size[] = { 1,1,1,2,4,8,1,1,2,4,8,4,8 }; + + if (count * size[type < 13 ? type:0] > 4) + fseek (ifp, get4()+base, SEEK_SET); + save = ftell(ifp); + printf("%*stag=0x%x %d, type=%d, count=%d, offset=%06x, data=", + level*2, "", tag, tag, type, count, save); + if (tag == 34310) goto quit; + if (type==2) putchar('\"'); + for (j = 0; j < count && j < DLEN; j++) + switch (type) { + case 1: case 6: case 7: /* byte values */ + printf ("%c%02x",(j & 31) || count < 17 ? ' ':'\n', fgetc(ifp) & 0xff); + break; + case 2: /* null-terminated ASCII strings */ + c = fgetc(ifp); + putchar(isprint(c) ? c:'.'); + break; + case 3: case 8: /* word values */ + printf ("%c%04x",(j & 15) || count < 9 ? ' ':'\n', get2()); + break; + case 4: case 9: /* dword values */ + printf ("%c%08x",(j & 7) || count < 5 ? ' ':'\n', get4()); + break; + case 5: case 10: /* rationals */ + num = get4(); + den = get4(); + printf (" %d/%d", num, den); +// printf (" %lf", (double) num/den); + break; + } + if (type==2) putchar('\"'); +quit: + putchar('\n'); + fseek (ifp, save, SEEK_SET); +} + +void parse_nikon_capture_note (int length) +{ + unsigned sorder, offset, tag, j, size; + + puts (" Nikon Capture Note:"); + sorder = order; + order = 0x4949; + fseek (ifp, 22, SEEK_CUR); + for (offset=22; offset+22 < length; offset += 22+size) { + tag = get4(); + fseek (ifp, 14, SEEK_CUR); + size = get4()-4; + printf(" tag=0x%08x, size=%d", tag, size); + for (j=0; j < size; j++) + printf ("%s%02x", j & 31 ? " ":"\n\t", fgetc(ifp)); + puts(""); + } + order = sorder; +} + +void nikon_decrypt (uchar ci, uchar cj, int tag, int i, int size, uchar *buf) +{ + static const uchar xlat[2][256] = { + { 0xc1,0xbf,0x6d,0x0d,0x59,0xc5,0x13,0x9d,0x83,0x61,0x6b,0x4f,0xc7,0x7f,0x3d,0x3d, + 0x53,0x59,0xe3,0xc7,0xe9,0x2f,0x95,0xa7,0x95,0x1f,0xdf,0x7f,0x2b,0x29,0xc7,0x0d, + 0xdf,0x07,0xef,0x71,0x89,0x3d,0x13,0x3d,0x3b,0x13,0xfb,0x0d,0x89,0xc1,0x65,0x1f, + 0xb3,0x0d,0x6b,0x29,0xe3,0xfb,0xef,0xa3,0x6b,0x47,0x7f,0x95,0x35,0xa7,0x47,0x4f, + 0xc7,0xf1,0x59,0x95,0x35,0x11,0x29,0x61,0xf1,0x3d,0xb3,0x2b,0x0d,0x43,0x89,0xc1, + 0x9d,0x9d,0x89,0x65,0xf1,0xe9,0xdf,0xbf,0x3d,0x7f,0x53,0x97,0xe5,0xe9,0x95,0x17, + 0x1d,0x3d,0x8b,0xfb,0xc7,0xe3,0x67,0xa7,0x07,0xf1,0x71,0xa7,0x53,0xb5,0x29,0x89, + 0xe5,0x2b,0xa7,0x17,0x29,0xe9,0x4f,0xc5,0x65,0x6d,0x6b,0xef,0x0d,0x89,0x49,0x2f, + 0xb3,0x43,0x53,0x65,0x1d,0x49,0xa3,0x13,0x89,0x59,0xef,0x6b,0xef,0x65,0x1d,0x0b, + 0x59,0x13,0xe3,0x4f,0x9d,0xb3,0x29,0x43,0x2b,0x07,0x1d,0x95,0x59,0x59,0x47,0xfb, + 0xe5,0xe9,0x61,0x47,0x2f,0x35,0x7f,0x17,0x7f,0xef,0x7f,0x95,0x95,0x71,0xd3,0xa3, + 0x0b,0x71,0xa3,0xad,0x0b,0x3b,0xb5,0xfb,0xa3,0xbf,0x4f,0x83,0x1d,0xad,0xe9,0x2f, + 0x71,0x65,0xa3,0xe5,0x07,0x35,0x3d,0x0d,0xb5,0xe9,0xe5,0x47,0x3b,0x9d,0xef,0x35, + 0xa3,0xbf,0xb3,0xdf,0x53,0xd3,0x97,0x53,0x49,0x71,0x07,0x35,0x61,0x71,0x2f,0x43, + 0x2f,0x11,0xdf,0x17,0x97,0xfb,0x95,0x3b,0x7f,0x6b,0xd3,0x25,0xbf,0xad,0xc7,0xc5, + 0xc5,0xb5,0x8b,0xef,0x2f,0xd3,0x07,0x6b,0x25,0x49,0x95,0x25,0x49,0x6d,0x71,0xc7 }, + { 0xa7,0xbc,0xc9,0xad,0x91,0xdf,0x85,0xe5,0xd4,0x78,0xd5,0x17,0x46,0x7c,0x29,0x4c, + 0x4d,0x03,0xe9,0x25,0x68,0x11,0x86,0xb3,0xbd,0xf7,0x6f,0x61,0x22,0xa2,0x26,0x34, + 0x2a,0xbe,0x1e,0x46,0x14,0x68,0x9d,0x44,0x18,0xc2,0x40,0xf4,0x7e,0x5f,0x1b,0xad, + 0x0b,0x94,0xb6,0x67,0xb4,0x0b,0xe1,0xea,0x95,0x9c,0x66,0xdc,0xe7,0x5d,0x6c,0x05, + 0xda,0xd5,0xdf,0x7a,0xef,0xf6,0xdb,0x1f,0x82,0x4c,0xc0,0x68,0x47,0xa1,0xbd,0xee, + 0x39,0x50,0x56,0x4a,0xdd,0xdf,0xa5,0xf8,0xc6,0xda,0xca,0x90,0xca,0x01,0x42,0x9d, + 0x8b,0x0c,0x73,0x43,0x75,0x05,0x94,0xde,0x24,0xb3,0x80,0x34,0xe5,0x2c,0xdc,0x9b, + 0x3f,0xca,0x33,0x45,0xd0,0xdb,0x5f,0xf5,0x52,0xc3,0x21,0xda,0xe2,0x22,0x72,0x6b, + 0x3e,0xd0,0x5b,0xa8,0x87,0x8c,0x06,0x5d,0x0f,0xdd,0x09,0x19,0x93,0xd0,0xb9,0xfc, + 0x8b,0x0f,0x84,0x60,0x33,0x1c,0x9b,0x45,0xf1,0xf0,0xa3,0x94,0x3a,0x12,0x77,0x33, + 0x4d,0x44,0x78,0x28,0x3c,0x9e,0xfd,0x65,0x57,0x16,0x94,0x6b,0xfb,0x59,0xd0,0xc8, + 0x22,0x36,0xdb,0xd2,0x63,0x98,0x43,0xa1,0x04,0x87,0x86,0xf7,0xa6,0x26,0xbb,0xd6, + 0x59,0x4d,0xbf,0x6a,0x2e,0xaa,0x2b,0xef,0xe6,0x78,0xb6,0x4e,0xe0,0x2f,0xdc,0x7c, + 0xbe,0x57,0x19,0x32,0x7e,0x2a,0xd0,0xb8,0xba,0x29,0x00,0x3c,0x52,0x7d,0xa8,0x49, + 0x3b,0x2d,0xeb,0x25,0x49,0xfa,0xa3,0xaa,0x39,0xa7,0xc5,0xa7,0x50,0x11,0x36,0xfb, + 0xc6,0x67,0x4a,0xf5,0xa5,0x12,0x65,0x7e,0xb0,0xdf,0xaf,0x4e,0xb3,0x61,0x7f,0x2f } }; + uchar ck=0x60; + + if (strncmp ((char *)buf, "02", 2)) return; + ci = xlat[0][ci]; + cj = xlat[1][cj]; + printf("Decrypted tag 0x%x:\n%*s", tag, (i & 31)*3, ""); + for (; i < size; i++) + printf("%02x%c", buf[i] ^ (cj += ci * ck++), (i & 31) == 31 ? '\n':' '); + if (size & 31) puts(""); +} + +int parse_tiff_ifd (int base, int level); + +void parse_makernote (base) +{ + int offset=0, entries, tag, type, count, val, save; + unsigned serial=0, key=0; + uchar buf91[630]="", buf97[608]="", buf98[31]=""; + short sorder; + char buf[10]; + +/* + The MakerNote might have its own TIFF header (possibly with + its own byte-order!), or it might just be a table. + */ + sorder = order; + fread (buf, 1, 10, ifp); + if (!strcmp (buf,"Nikon")) { /* starts with "Nikon\0\2\0\0\0" ? */ + base = ftell(ifp); + order = get2(); /* might differ from file-wide byteorder */ + val = get2(); /* should be 42 decimal */ + offset = get4(); + fseek (ifp, offset-8, SEEK_CUR); + } else if (!strncmp (buf,"FUJIFILM",8) || + !strncmp (buf,"SONY",4) || + !strcmp (buf,"Panasonic")) { + order = 0x4949; + fseek (ifp, 2, SEEK_CUR); + } else if (!strcmp (buf,"OLYMP") || + !strcmp (buf,"LEICA") || + !strcmp (buf,"Ricoh") || + !strcmp (buf,"EPSON")) + fseek (ifp, -2, SEEK_CUR); + else if (!strcmp (buf,"AOC")) + fseek (ifp, -4, SEEK_CUR); + else + fseek (ifp, -10, SEEK_CUR); + + entries = get2(); + if (entries > 100) return; + puts(" MakerNote:"); + while (entries--) { + save = ftell(ifp); + tag = get2(); + type = get2(); + count= get4(); + tiff_dump (base, tag, type, count, 2); + if (tag == 0x1d) + while ((val = fgetc(ifp))) + serial = serial*10 + (isdigit(val) ? val - '0' : val % 10); + if (tag == 0x91) + fread (buf91, sizeof buf91, 1, ifp); + if (tag == 0x97) + fread (buf97, sizeof buf97, 1, ifp); + if (tag == 0x98) + fread (buf98, sizeof buf98, 1, ifp); + if (tag == 0xa7) + key = fgetc(ifp)^fgetc(ifp)^fgetc(ifp)^fgetc(ifp); + if (!strcmp (buf,"OLYMP") && tag >> 8 == 0x20) + parse_tiff_ifd (base, 3); + if (tag == 0xe01) + parse_nikon_capture_note (count); + if (tag == 0xb028) { + fseek (ifp, get4(), SEEK_SET); + parse_tiff_ifd (base, 3); + } + fseek (ifp, save+12, SEEK_SET); + } + nikon_decrypt (serial, key, 0x91, 4, sizeof buf91, buf91); + if (!strncmp ((char *)buf97, "0205", 4)) + nikon_decrypt (serial, key, 0x97, 4, 284, buf97); + else + nikon_decrypt (serial, key, 0x97, 284, sizeof buf97, buf97); + nikon_decrypt (serial, key, 0x98, 4, sizeof buf98, buf98); + order = sorder; +} + +void parse_exif(int base) +{ + int entries, tag, type, count, save; + + puts("EXIF table:"); + entries = get2(); + while (entries--) { + save = ftell(ifp); + tag = get2(); + type = get2(); + count= get4(); + tiff_dump (base, tag, type, count, 1); + if (tag == 0x927c) + parse_makernote (base); + fseek (ifp, save+12, SEEK_SET); + } +} + +void parse_mos(int level); + +void sony_decrypt (unsigned *data, int len, int start, int key) +{ + static unsigned pad[128], p; + + if (start) { + for (p=0; p < 4; p++) + pad[p] = key = key * 48828125 + 1; + pad[3] = pad[3] << 1 | (pad[0]^pad[2]) >> 31; + for (p=4; p < 127; p++) + pad[p] = (pad[p-4]^pad[p-2]) << 1 | (pad[p-3]^pad[p-1]) >> 31; + for (p=0; p < 127; p++) + pad[p] = htonl(pad[p]); + } + while (len--) + *data++ ^= pad[p++ & 127] = pad[(p+1) & 127] ^ pad[(p+65) & 127]; +} + +int parse_tiff_ifd (int base, int level) +{ + int entries, tag, type, count, slen, save, save2, i; + unsigned *buf, sony_offset=0, sony_length=0, sony_key=0; + FILE *sfp; + + entries = get2(); + if (entries > 1024) return 1; + while (entries--) { + save = ftell(ifp); + tag = get2(); + type = get2(); + count= get4(); + slen = count; + if (slen > 128) slen = 128; + tiff_dump (base, tag, type, count, level); + switch (tag) { + case 0x10f: /* Make tag */ + fgets (make, slen, ifp); + break; + case 0x110: /* Model tag */ + fgets (model, slen, ifp); + break; + case 33405: /* Model2 tag */ + fgets (model2, slen, ifp); + break; + case 0x14a: /* SubIFD tag */ + save2 = ftell(ifp); + for (i=0; i < count; i++) { + printf ("SubIFD #%d:\n", i+1); + fseek (ifp, save2 + i*4, SEEK_SET); + fseek (ifp, get4()+base, SEEK_SET); + parse_tiff_ifd (base, level+1); + } + break; + case 29184: sony_offset = get4(); break; + case 29185: sony_length = get4(); break; + case 29217: sony_key = get4(); break; + case 33424: + puts("Kodak private data:"); + fseek (ifp, get4()+base, SEEK_SET); + parse_tiff_ifd (base, level+1); + break; + case 34310: + parse_mos(0); + break; + case 34665: + fseek (ifp, get4()+base, SEEK_SET); + parse_exif (base); + break; + case 50706: + is_dng = 1; + break; + case 50740: + if (count != 4 || type != 1) break; + puts("Sony SR2 private IFD:"); + fseek (ifp, get4()+base, SEEK_SET); + parse_tiff_ifd (base, level+1); + } + fseek (ifp, save+12, SEEK_SET); + } + if (sony_length && (buf = malloc(sony_length))) { + fseek (ifp, sony_offset, SEEK_SET); + fread (buf, sony_length, 1, ifp); + sony_decrypt (buf, sony_length/4, 1, sony_key); + sfp = ifp; + if ((ifp = tmpfile())) { + fwrite (buf, sony_length, 1, ifp); + fseek (ifp, 0, SEEK_SET); + puts ("Sony SR2 encrypted IFD:"); + parse_tiff_ifd (-sony_offset, level); + fclose (ifp); + } + ifp = sfp; + free (buf); + } + return 0; +} + +/* + Parse a TIFF file looking for camera model and decompress offsets. + */ +void parse_tiff (int base) +{ + int doff, ifd=0; + + fseek (ifp, base, SEEK_SET); + order = get2(); + if (order != 0x4949 && order != 0x4d4d) return; + get2(); + while ((doff = get4())) { + fseek (ifp, doff+base, SEEK_SET); + printf ("IFD #%d:\n", ifd++); + if (parse_tiff_ifd (base, 0)) break; + } +} + +void parse_minolta() +{ + int data_offset, save, tag, len; + + fseek (ifp, 4, SEEK_SET); + data_offset = get4() + 8; + while ((save=ftell(ifp)) < data_offset) { + tag = get4(); + len = get4(); + printf ("Tag %c%c%c offset %06x length %06x\n", + tag>>16, tag>>8, tag, save, len); + switch (tag) { + case 0x545457: /* TTW */ + parse_tiff (ftell(ifp)); + } + fseek (ifp, save+len+8, SEEK_SET); + } +} + +/* + Parse the CIFF structure. + */ +void parse_ciff (int offset, int length, int level) +{ + int tboff, nrecs, i, j, type, len, dlen, roff, aoff=0, save; + char c, name[256]; + ushort key[2]; + + fseek (ifp, offset+length-4, SEEK_SET); + tboff = get4() + offset; + fseek (ifp, tboff, SEEK_SET); + nrecs = get2(); + if (nrecs > 100) return; + printf ("%*s%d records:\n", level*2, "", nrecs); + for (i = 0; i < nrecs; i++) { + save = ftell(ifp); + type = get2(); + printf ("%*stype=0x%04x", level*2, "", type); + if (type & 0x4000) { + len = 8; + type &= 0x3fff; + } else { + len = get4(); + roff = get4(); + aoff = offset + roff; + printf (", length=%d, reloff=%d, absoff=%d", + len, roff, aoff); + fseek (ifp, aoff, SEEK_SET); + } + if ((type & 0xe700) == 0) + printf (", data="); + if (type == 0x0032) /* display as words */ + type |= 0x1000; + dlen = len < DLEN ? len:DLEN; + switch (type >> 8) { + case 0x28: + case 0x30: + putchar('\n'); + parse_ciff (aoff, len, level+1); + fseek (ifp, save+10, SEEK_SET); + continue; + case 0x00: /* byte values */ + for (j = 0; j < dlen; j++) + printf ("%c%02x",(j & 31) || dlen < 16 ? ' ':'\n', fgetc(ifp) & 0xff); + break; + case 0x08: /* null-terminated ASCII strings */ + putchar('\"'); + for (j = 0; j < dlen; j++) { + c = fgetc(ifp); + putchar( isprint(c) ? c:'.'); + } + putchar('\"'); + break; + case 0x10: /* word values */ + key[0] = get2(); + fseek (ifp, -2, SEEK_CUR); + if (type == 0x1032 && key[0] == 1040) + key[1] = 17907; + else key[0] = key[1] = 0; + for (j = 0; j < dlen; j+=2) + printf ("%c%5u",(j & 31) || dlen < 16 ? ' ':'\n', + get2() ^ key[(j >> 1) & 1]); + break; + case 0x18: /* dword values */ + for (j = 0; j < dlen; j+=4) + printf ("%c%08x",(j & 31) || dlen < 16 ? ' ':'\n', get4()); + } + putchar('\n'); + fseek (ifp, save+10, SEEK_SET); + if (type == 0x080a) { /* Get the camera name */ + fseek (ifp, aoff, SEEK_SET); + fread (name, 256, 1, ifp); + strcpy (make, name); + strcpy (model, name + strlen(make)+1); + } + } +} + +int parse_jpeg (int offset) +{ + int len, save, hlen; + + fseek (ifp, offset, SEEK_SET); + if (fgetc(ifp) != 0xff || fgetc(ifp) != 0xd8) return 0; + + while (fgetc(ifp) == 0xff && fgetc(ifp) >> 4 != 0xd) { + order = 0x4d4d; + len = get2() - 2; + save = ftell(ifp); + order = get2(); + hlen = get4(); + if (get4() == 0x48454150) /* "HEAP" */ + parse_ciff (save+hlen, len-hlen, 0); + parse_tiff (save+6); + fseek (ifp, save+len, SEEK_SET); + } + return 1; +} + +void parse_riff (int level) +{ + unsigned i, size, end, save; + char tag[4], type[4], buf[64]; + + order = 0x4949; + fread (tag, 4, 1, ifp); + size = get4(); + if (isdigit(tag[0])) { + fseek (ifp, size, SEEK_CUR); + return; + } + printf ("%*.4s size %d", level*4+4, tag, size); + if (!memcmp(tag,"RIFF",4) || !memcmp(tag,"LIST",4)) { + end = ftell(ifp) + size; + fread (type, 4, 1, ifp); + printf (" type %.4s:\n", type); + while (ftell(ifp) < end) + parse_riff (level+1); + } else { + save = ftell(ifp); + fread (buf, 1, 40, ifp); + printf (": "); + for (i=0; i < 40 && isprint(buf[i]); i++) + putchar (buf[i]); + putchar ('\n'); + fseek (ifp, save+size, SEEK_SET); + } +} + +void parse_mos(int level) +{ + char data[256]; + int i, skip, save; + char *cp; + + save = ftell(ifp); + while (1) { + if (get4() != 0x504b5453) break; + get4(); + printf ("%*sPKTS ", level, ""); + fread (data, 1, 40, ifp); + skip = get4(); + printf ("%s %d bytes: ", data, skip); + if (!strcmp(data,"pattern_ratation_angle")) { + printf ("%d\n", get2()); + continue; + } + if (!strcmp(data,"icc_camera_to_tone_matrix")) { + for (i=0; i < skip/4; i++) + printf ("%f ", int_to_float(get4())); + putchar('\n'); + continue; + } + fread (data, 1, sizeof data, ifp); + fseek (ifp, -sizeof data, SEEK_CUR); + data[sizeof data - 1] = 0; + while ((cp=strchr(data,'\n'))) + *cp = ' '; + printf ("%s\n",data); + parse_mos(level+2); + fseek (ifp, skip, SEEK_CUR); + } + fseek (ifp, save, SEEK_SET); +} + +void parse_rollei() +{ + char line[128], *val; + + fseek (ifp, 0, SEEK_SET); + do { + fgets (line, 128, ifp); + fputs (line, stdout); + if ((val = strchr(line,'='))) + *val++ = 0; + else + val = line + strlen(line); + } while (strncmp(line,"EOHD",4)); + strcpy (make, "Rollei"); + strcpy (model, "d530flex"); +} + +void get_utf8 (int offset, char *buf, int len) +{ + ushort c; + char *cp; + + fseek (ifp, offset, SEEK_SET); + for (cp=buf; (c = get2()) && cp+3 < buf+len; ) { + if (c < 0x80) + *cp++ = c; + else if (c < 0x800) { + *cp++ = 0xc0 + (c >> 6); + *cp++ = 0x80 + (c & 0x3f); + } else { + *cp++ = 0xe0 + (c >> 12); + *cp++ = 0x80 + (c >> 6 & 0x3f); + *cp++ = 0x80 + (c & 0x3f); + } + } + *cp = 0; +} + +void parse_foveon() +{ + int entries, off, len, tag, save, i, j, k, pent, poff[256][2]; + char name[128], value[128], camf[0x20000], *pos, *cp, *dp; + unsigned val, key, type, num, ndim, dim[3]; + + order = 0x4949; /* Little-endian */ + fseek (ifp, -4, SEEK_END); + fseek (ifp, get4(), SEEK_SET); + if (get4() != 0x64434553) { /* SECd */ + printf ("Bad Section identifier at %6x\n", (int)ftell(ifp)-4); + return; + } + get4(); + entries = get4(); + while (entries--) { + off = get4(); + len = get4(); + tag = get4(); + save = ftell(ifp); + fseek (ifp, off, SEEK_SET); + printf ("%c%c%c%c at offset %06x, length %06x, ", + tag, tag >> 8, tag >> 16, tag >> 24, off, len); + if (get4() != (0x20434553 | (tag << 24))) { + printf ("Bad Section identifier at %6x\n", off); + goto next; + } + val = get4(); + printf ("version %d.%d, ",val >> 16, val & 0xffff); + switch (tag) { + case 0x32414d49: /* IMA2 */ + case 0x47414d49: /* IMAG */ + printf ("type %d, " , get4()); + printf ("format %2d, " , get4()); + printf ("columns %4d, " , get4()); + printf ("rows %4d, " , get4()); + printf ("rowsize %d\n" , get4()); + parse_jpeg (off+28); + order = 0x4949; + break; + case 0x464d4143: /* CAMF */ + printf ("type %d, ", get4()); + get4(); + for (i=0; i < 4; i++) + putchar(fgetc(ifp)); + val = get4(); + printf (" version %d.%d:\n",val >> 16, val & 0xffff); + key = get4(); + if ((len -= 28) > 0x20000) + len = 0x20000; + fread (camf, 1, len, ifp); + for (i=0; i < len; i++) { + key = (key * 1597 + 51749) % 244944; + val = key * (INT64) 301593171 >> 24; + camf[i] ^= ((((key << 8) - val) >> 1) + val) >> 17; + } + for (pos=camf; (unsigned) (pos-camf) < len; pos += sget4(pos+8)) { + if (strncmp (pos, "CMb", 3)) { + printf("Bad CAMF tag \"%.4s\"\n", pos); + break; + } + val = sget4(pos+4); + printf (" %4.4s version %d.%d: ", pos, val >> 16, val & 0xffff); + switch (pos[3]) { + case 'M': + cp = pos + sget4(pos+16); + type = sget4(cp); + ndim = sget4(cp+4); + dim[0] = dim[1] = dim[2] = 1; + printf ("%d-dimensonal array %s of type %d:\n Key: (", + ndim, pos+sget4(pos+12), sget4(cp)); + dp = pos + sget4(cp+8); + for (i=ndim; i--; ) { + cp += 12; + dim[i] = sget4(cp); + printf ("%s %d%s", pos+sget4(cp+4), dim[i], i ? ", ":")\n"); + } + for (i=0; i < dim[2]; i++) { + for (j=0; j < dim[1]; j++) { + printf (" "); + for (k=0; k < dim[0]; k++) + switch (type) { + case 5: + printf ("%7d", *(uchar *)dp++); + break; + case 0: + case 6: + printf ("%7d", (short) sget2(dp)); + dp += 2; + break; + case 1: + case 2: + printf (" %d", sget4(dp)); + dp += 4; + break; + case 3: + val = sget4(dp); + printf (" %9f", int_to_float(val)); + dp += 4; + } + printf ("\n"); + } + printf ("\n"); + } + break; + case 'P': + val = sget4(pos+16); + num = sget4(pos+val); + printf ("%s, %d parameters:\n", pos+sget4(pos+12), num); + cp = pos+val+8 + num*8; + for (i=0; i < num; i++) { + val += 8; + printf (" %s = %s\n", cp+sget4(pos+val), cp+sget4(pos+val+4)); + } + break; + case 'T': + cp = pos + sget4(pos+16); + printf ("%s = %.*s\n", pos+sget4(pos+12), sget4(cp), cp+4); + break; + default: + printf ("\n"); + } + } + break; + case 0x504f5250: /* PROP */ + printf ("entries %d, ", pent=get4()); + printf ("charset %d, ", get4()); + get4(); + printf ("nchars %d\n", get4()); + off += pent*8 + 24; + if (pent > 256) pent=256; + for (i=0; i < pent*2; i++) + poff[0][i] = off + get4()*2; + for (i=0; i < pent; i++) { + get_utf8 (poff[i][0], name, 128); + get_utf8 (poff[i][1], value, 128); + printf (" %s = %s\n", name, value); + if (!strcmp (name,"CAMMANUF")) + strcpy (make, value); + if (!strcmp (name,"CAMMODEL")) + strcpy (model, value); + } + } +next: + fseek (ifp, save, SEEK_SET); + } +} + +void parse_fuji (int offset) +{ + int entries, tag, len; + + fseek (ifp, offset, SEEK_SET); + if (!(len = get4())) return; + printf ("Fuji table at %d:\n",len); + fseek (ifp, len, SEEK_SET); + entries = get4(); + if (entries > 255) return; + while (entries--) { + tag = get2(); + len = get2(); + printf ("Fuji tag=0x%x, len=%d, data =",tag,len); + while (len--) + printf (" %02x",fgetc(ifp)); + putchar ('\n'); + } +} + +void parse_phase_one (int base) +{ + unsigned entries, tag, type, len, data, save; + unsigned meta=0, wide=0, high=0, i, j; + char str[256]; + + fseek (ifp, base, SEEK_SET); + order = get4() & 0xffff; + if (get4() >> 8 != 0x526177) return; + fseek (ifp, base+get4(), SEEK_SET); + entries = get4(); + get4(); + while (entries--) { + tag = get4(); + type = get4(); + len = get4(); + data = get4(); + save = ftell(ifp); + printf ("Phase One tag=0x%x, type=%d, len=%2d, data = 0x%x", + tag, type, len, data); + if (type == 4 && len == 4 && data > 0xfffffff) + printf (" = %f", int_to_float(data)); + putchar ('\n'); + if (tag == 0x110) meta = base+data; + if (len > 4) + fseek (ifp, base+data, SEEK_SET); + if (type == 1 && len < 256) { + fread (str, 256, 1, ifp); + puts (str); + } + if (tag != 0x21c && type == 4 && len > 4) { + for ( ; len > 0; len -= 4) + printf ("%f ", int_to_float(get4())); + puts (""); + } + fseek (ifp, save, SEEK_SET); + } + strcpy (make, "Phase One"); + strcpy (model, "unknown"); + if (!meta) return; + fseek (ifp, meta, SEEK_SET); + order = get2(); + fseek (ifp, 6, SEEK_CUR); + fseek (ifp, meta+get4(), SEEK_SET); + entries = get4(); + get4(); + while (entries--) { + tag = get4(); + len = get4(); + data = get4(); + save = ftell(ifp); + printf ("Phase One meta tag=0x%x, len=%2d, offset = 0x%x, data = ", + tag, len, data); + if (!((0x000801f4 >> (tag-0x400)) & 1)) putchar ('\n'); + fseek (ifp, meta+data, SEEK_SET); + switch (tag) { + case 0x400: + for (i=0; i < len; i+=2) + printf ("%5u%c", get2(), (i & 6) == 6 || i == len-2 ? '\n':' '); + break; + case 0x401: + for (i=0; i < 16; i+=2) + printf ("%6u%c", get2(), (i & 14) == 14 || i == len-2 ? '\n':' '); + for (; i < len; i+=4) + printf ("%9.6f%c", int_to_float(get4()), + (i & 28) == 12 || i == len-4 ? '\n':' '); + break; + case 0x402: + printf ("%f\n", int_to_float (data)); + break; + case 0x404: case 0x405: case 0x406: case 0x407: + fread (str, 256, 1, ifp); + puts (str); + break; + case 0x408: case 0x413: + printf ("%lf\n", get_double()); + break; + case 0x40b: case 0x410: case 0x416: + for (i=0; i < len; i+=2) + printf ("%6u%c", get2(), (i & 14) == 14 || i == len-2 ? '\n':' '); + break; + case 0x40f: case 0x418: case 0x419: case 0x41a: + for (i=0; i < 4; i++) + printf ("%02X%c", fgetc(ifp), i == 3 ? '\n':' '); + for (; i < len; i+=4) + printf ("%e%c", int_to_float(get4()), i == len-4 ? '\n':' '); + break; + case 0x412: + for (i=0; i < 36; i+=4) { + printf ("%u ", j=get4()); + if (i == 4) wide = j; + if (i == 12) high = j*2; + } + printf ("%u\n", get2()); + for (i=0; i < wide*high; i++) + printf ("%9.6f%c", int_to_float(get4()), + i % wide == wide-1 ? '\n':' '); + for (i=0; i < wide*high; i++) + printf ("%5u%c", get2(), i % wide == wide-1 ? '\n':' '); + break; + default: + for (i=0; i < len; i++) + printf ("%02X%c", fgetc(ifp), + (i & 15) == 15 || i == len-1 ? '\n':' '); + } + fseek (ifp, save, SEEK_SET); + } +} + +char *memmem (char *haystack, size_t haystacklen, + char *needle, size_t needlelen) +{ + char *c; + for (c = haystack; c <= haystack + haystacklen - needlelen; c++) + if (!memcmp (c, needle, needlelen)) + return c; + return NULL; +} + +/* + Identify which camera created this file, and set global variables + accordingly. Return nonzero if the file cannot be decoded. + */ +void identify() +{ + char head[32], *cp; + unsigned hlen, fsize, toff, tlen; + + make[0] = model[0] = model2[0] = is_dng = 0; + order = get2(); + hlen = get4(); + fseek (ifp, 0, SEEK_SET); + fread (head, 1, 32, ifp); + fseek (ifp, 0, SEEK_END); + fsize = ftell(ifp); + if ((cp = memmem (head, 32, "MMMM", 4)) || + (cp = memmem (head, 32, "IIII", 4))) { + parse_phase_one (cp-head); + if (cp-head) parse_tiff (0); + } else if (order == 0x4949 || order == 0x4d4d) { + if (!memcmp(head+6,"HEAPCCDR",8)) { + parse_ciff (hlen, fsize - hlen, 0); + fseek (ifp, hlen, SEEK_SET); + } else + parse_tiff (0); + } else if (!memcmp (head,"NDF0",4)) { + parse_tiff (12); + } else if (!memcmp (head,"\0MRM",4)) { + parse_minolta(); + } else if (!memcmp (head,"FUJIFILM",8)) { + fseek (ifp, 84, SEEK_SET); + toff = get4(); + tlen = get4(); + parse_fuji (92); + if (toff > 120) parse_fuji (120); + parse_tiff (toff+12); + } else if (!memcmp (head,"RIFF",4)) { + fseek (ifp, 0, SEEK_SET); + parse_riff(0); + } else if (!memcmp (head,"DSC-Image",9)) + parse_rollei(); + else if (!memcmp (head,"FOVb",4)) + parse_foveon(); + parse_jpeg(0); +} + +int main(int argc, char **argv) +{ + int arg; + + if (argc == 1) + { + fprintf (stderr, + "\nRaw Photo Parser" + "\nby Dave Coffin, dcoffin a cybercom o net" + "\n\nUsage: %s file1.crw file2.crw ...\n", argv[0]); + return 1; + } + for (arg=1; arg < argc; arg++) + { + fname = argv[arg]; + ifp = fopen (fname,"rb"); + if (!ifp) { + perror (fname); + continue; + } + printf ("\nParsing %s:\n", fname); + identify(); + fclose (ifp); + } + return 0; +} diff --git a/ready b/ready new file mode 100644 index 0000000..473a0f4 diff --git a/update_dcraw b/update_dcraw new file mode 100644 index 0000000..685e1f5 --- /dev/null +++ b/update_dcraw @@ -0,0 +1,20 @@ +#! /bin/sh + +# Call this script to update dcraw to latest version. + +wget -N http://www.cybercom.net/~dcoffin/dcraw/dcraw.1 +wget -N http://www.cybercom.net/~dcoffin/dcraw/parse.c +wget -N http://www.cybercom.net/~dcoffin/dcraw/fujiturn.c +wget -N http://www.cybercom.net/~dcoffin/dcraw/dcwrap +wget -N http://www.cybercom.net/~dcoffin/dcraw/dcraw.c +wget -N http://www.cybercom.net/~dcoffin/dcraw/.badpixels +mv _badpixels badpixels +( URL="http://www.cybercom.net/~dcoffin/dcraw/" ; echo -e "$URL:\n" ; LC_ALL=C lynx -dump "$URL" | sed '1d;3,4d' ) >README +wget http://www.cybercom.net/~dcoffin/dcraw/RCS/dcraw.c,v +rlog dcraw.c >dcraw.log +echo +echo "Please check, whether packaged file list is up to date." +echo "Don't forget to change the version in dcraw.spec." $(head -n 1 dcraw.c,v) +echo "You can look at dcraw.log for the complete change log." +echo "Before submit, please call:" +echo "rm dcraw.c,v dcraw.log"