diff options
author | Theodore Kilgore <kilgota@auburn.edu> | 2010-04-20 14:41:27 +0000 |
---|---|---|
committer | Theodore Kilgore <kilgota@auburn.edu> | 2010-04-20 14:41:27 +0000 |
commit | bae29870f863054ac44863fcf07eba598f3b21ff (patch) | |
tree | 3b45ccb43d38d0568fd2db096ebd37565c2e5db1 /camlibs/jl2005c | |
parent | d11333a841268aa55c5f425054aff715f7ea89e3 (diff) | |
download | libgphoto2-bae29870f863054ac44863fcf07eba598f3b21ff.tar.gz |
jl2005c:Decompression enabled. Camlib is fully functional
git-svn-id: https://svn.code.sf.net/p/gphoto/code/trunk/libgphoto2@13007 67ed7778-7388-44ab-90cf-0a291f65f57c
Diffstat (limited to 'camlibs/jl2005c')
-rw-r--r-- | camlibs/jl2005c/ChangeLog | 14 | ||||
-rw-r--r-- | camlibs/jl2005c/Makefile-files | 10 | ||||
-rw-r--r-- | camlibs/jl2005c/README.jl2005bcd-compression | 342 | ||||
-rw-r--r-- | camlibs/jl2005c/README.jl2005c | 266 | ||||
-rw-r--r-- | camlibs/jl2005c/img_enhance.c | 252 | ||||
-rw-r--r-- | camlibs/jl2005c/img_enhance.h | 35 | ||||
-rw-r--r-- | camlibs/jl2005c/jl2005bcd_decompress.c | 280 | ||||
-rw-r--r-- | camlibs/jl2005c/jl2005bcd_decompress.h | 30 | ||||
-rw-r--r-- | camlibs/jl2005c/jl2005c.c | 390 | ||||
-rw-r--r-- | camlibs/jl2005c/jl2005c.h | 32 | ||||
-rw-r--r-- | camlibs/jl2005c/jpeg_memsrcdest.c | 309 | ||||
-rw-r--r-- | camlibs/jl2005c/jpeg_memsrcdest.h | 9 | ||||
-rw-r--r-- | camlibs/jl2005c/library.c | 168 |
13 files changed, 1655 insertions, 482 deletions
diff --git a/camlibs/jl2005c/ChangeLog b/camlibs/jl2005c/ChangeLog index 9483beef5..b564b5568 100644 --- a/camlibs/jl2005c/ChangeLog +++ b/camlibs/jl2005c/ChangeLog @@ -1,3 +1,17 @@ +2010-04-19 Theodore Kilgore <kilgota@auburn.edu> + * README.jl2005c: revised. + * README.jl2005bcd-compression: added. + * img_enhance.[c,h]: added. + * jl2005bcd_decompress.[c,h]: added. Decompresses data, autodetects + thumbnails if present in raw data, can also process the + thumbnails. + * jpeg_memsrcdest.[c,h]: added (helper files for decompression). + NOTE: Decompression algorithm is completely solved. Camlib + should be added as "standard" part of libgphoto2. + +2010-04-18 Theodore Kilgore <kilgota@auburn.edu> + * library.c: Yet another camera found. + 2010-04-14 Theodore Kilgore <kilgota@auburn.edu> * library.c: Yet another camera found. diff --git a/camlibs/jl2005c/Makefile-files b/camlibs/jl2005c/Makefile-files index 82fa634c4..2d6547787 100644 --- a/camlibs/jl2005c/Makefile-files +++ b/camlibs/jl2005c/Makefile-files @@ -1,11 +1,15 @@ -EXTRA_DIST += jl2005c/README.jl2005c -camlibdoc_DATA += jl2005c/README.jl2005c +EXTRA_DIST += jl2005c/README.jl2005c jl2005c/README.jl2005bcd-compression +camlibdoc_DATA += jl2005c/README.jl2005c jl2005c/README.jl2005bcd-compression EXTRA_LTLIBRARIES += jl2005c.la jl2005c_la_SOURCES = jl2005c/library.c\ jl2005c/jl2005c.c\ - jl2005c/jl2005c.h + jl2005c/jl2005bcd_decompress.c\ + jl2005c/jl2005bcd_decompress.h\ + jl2005c/jl2005c.h\ + jl2005c/img_enhance.c\ + jl2005c/img_enhance.h jl2005c_la_LDFLAGS = $(camlib_ldflags) jl2005c_la_DEPENDENCIES = $(camlib_dependencies) jl2005c_la_LIBADD = $(camlib_libadd) diff --git a/camlibs/jl2005c/README.jl2005bcd-compression b/camlibs/jl2005c/README.jl2005bcd-compression new file mode 100644 index 000000000..134b74df7 --- /dev/null +++ b/camlibs/jl2005c/README.jl2005bcd-compression @@ -0,0 +1,342 @@ +History of the reverse engineering of the compression used in the + JL2005B, JL2005C, and JL2005D cameras +----------------------------------------------------------- + +All of these cameras use a compression format which is derived from JPEG +but is in several respects non-standard, making the job of understanding +it very difficult. + +Thus, after several years have passed by, during which it has been possible +to get the raw files out of these cameras but not possible to do anything +with the data in the raw files, a task force was started to deal with the +problem. The results of the reverse engineering work by Adam Rubin and +Bertrik Sikken were shared by means of a wiki page with Hans de Goede +and me (Theodore Kilgore). What is known as of April 10, 2010 is listed +at + +<http://sourceforge.net/apps/mediawiki/gphoto/index.php?title=ImageEncoding +Jeilin2005CD> + +A dump of this page is given below. + +The description provided in this webpage was used to write the decompression +code. The objective was to provide output in a standard, uncompressed bitmap +format in order to provide the opportunity to apply whatever techniques for +image enhancement or postprocessing that might be needed. The format chosen +for this was PPM, used in many other camera libraries in libgphoto2. + +Faced with the choice of incorporating a modified variant of libjpeg in +the decompression code, for ultimate inclusion in libgphoto2/camlibs/jl2005c, +or else finding a way to use the standard library functions of libjpeg, +Hans de Goede found a way to use the standard library functions with minimal +modification. + +The details of how the decompression algorithm needs to work are well laid +out in the dump of the wiki web page (below). See especially the section +called "Entropy coded data." + +A modified version of the file jpeg_memsrcdest.c is part of the +decompression package here. The copyright notice of the original file is +preserved inside the file, and it is also copied here: + +/* + * jpeg_memsrcdest.c and jidctflt.c + * + * Copyright (C) 1994-1998, Thomas G. Lane. + * This file is part of the Independent JPEG Group's software. + * + * The authors make NO WARRANTY or representation, either express or implied, + * with respect to this software, its quality, accuracy, merchantability, or + * fitness for a particular purpose. This software is provided "AS IS", and you, + * its user, assume the entire risk as to its quality and accuracy. + * + * This software is copyright (C) 1991-1998, Thomas G. Lane. + * All Rights Reserved except as specified below. + * + * Permission is hereby granted to use, copy, modify, and distribute this + * software (or portions thereof) for any purpose, without fee, subject to these + * conditions: + * (1) If any part of the source code for this software is distributed, then this + * README file must be included, with this copyright and no-warranty notice + * unaltered; and any additions, deletions, or changes to the original files + * must be clearly indicated in accompanying documentation. + * (2) If only executable code is distributed, then the accompanying + * documentation must state that "this software is based in part on the work of + * the Independent JPEG Group". + * (3) Permission for use of this software is granted only if the user accepts + * full responsibility for any undesirable consequences; the authors accept + * NO LIABILITY for damages of any kind. + * + * These conditions apply to any software derived from or based on the IJG code, + * not just to the unmodified library. If you use our work, you ought to + * acknowledge us. + * + * Permission is NOT granted for the use of any IJG author's name or company name + * in advertising or publicity relating to this software or products derived from + * it. This software may be referred to only as "the Independent JPEG Group's + * software". + * + * We specifically permit and encourage the use of this software as the basis of + * commercial products, provided that all warranty or liability claims are + * assumed by the product vendor. + */ + + +Below is a dump of: +<http://sourceforge.net/apps/mediawiki/gphoto/index.php?title=ImageEncoding +Jeilin2005CD> +Made on 10 April 2010. + +mageEncodingJeilin2005CD +From gphoto +Jump to: navigation, search +Contents + + * 1 Introduction + * 2 Image format + o 2.1 Image header + o 2.2 Thumbnail image + o 2.3 Entropy coded data + + 2.3.1 Huffman table + + 2.3.2 Quantisation table + +Introduction + +This page describes the image format used in cameras with the Jeilin 2005 +B/C/D chip. + +This chip stores images in a non-standard JPEG-like format. To understand +the information on this page, it is recommended to have a basic +understanding of JPEG compression. +Image format + +The image file seems to consist of the following parts: + + * a 16-byte image header + * some kind of thumbnail of the actual image + * entropy-coded JPEG-like image data + +These parts are described in the following paragraphs. +Image header + +The 16-byte image header is the very first thing in an raw image file +(copied directly from the PAT) and contains basic image meta-data. All words +are stored big-endian, and block size is 0x80 bytes for the JL2005B and +0x200 bytes for the JL2005C and JL2005D chips. "Something happens" means +that I don't know yet what, if any, the effects are. Additions and +corrections to this section are welcome! +Byte Meaning +0x00 Always observed as zero so far, but something happens if it's 0x01 +or 0x21, and something else happens if it's 0x11. +0x01 Camera ID or attributes? If bit 4 is set, gamma correction is +enabled. If a value elsewhere is set, then if bit 7 is set, then bits 2 and +3 do something. +0x02 Bit 4: 0 = lower compression ratio, 1 = higher compression ratio + +Bits 0-1: resolution of photo (camera-dependent), 00 = CIF or VGA, 01 = QCIF +or QVGA, 02 = SVGA if supported + +All other bits have always been observed as zero so far. +0x03 Bits 0-6 = image quality factor (100% is best quality), specifics +below under "Quantization Table" + +Bit 7: if set, read the raw file into memory but do not decode it +0x04 Image height, in units of 8 pixels +0x05 Image width, in units of 8 pixels +0x06-0x07 Total size of image data in blocks, including thumbnail if +any +0x08 When writing the raw file, if byte 0x03 bit 7 is set (decode image) +and byte 0x02 bit 4 is set (higher compression), then a workspace area of ( +width * (photo height in pixels - this byte) / 2 ) bytes is allocated, +instead of width * height. + +When reading in the raw file, if byte 0x03 bit 7 is clear and byte 0x02 bit +4 is clear, the value used later on for image height in pixels follows the +same equation. + +I don't (yet) know why either of these are done. +0x09 If there's a thumbnail image (as detailed below), the thumbnail's +length in blocks is this byte divided by four. So far, 0x60 or 0xf0 for this +byte means a thumbnail (resolution 96x64 or 128x120 pixels respectively). +The significance of any other value is not known. +0x0a Another camera ID or attributes? Bits 0-1 do something. +0x0b Unknown -- always observed as zero so far +0x0c-0x0d Starting address of photo in storage medium, in blocks. +0x0e-0x0f Word, function unknown. + +(part of this table was derived from the jl2005c code already in gphoto) +Thumbnail image + +Directly following the image header, sometimes, is a thumbnail image. See +"Image Header" byte 0x09, above, to detect whether one is present. + +From observation: + + * JL2005B -- no thumbnails + * JL2005C -- some have thumbnails; if so, 96x64 pixels + * JL2005D -- many have thumbnails; if so, 128x120 pixels + +Note that the aspect ratio of the thumbnails is not necessarily the same as +that of the picture. + +Pixels in the thumbnail are encoded left-to-right, top-to-bottom. Each pixel +of the thumbnail image is represented by a 16-bit word, big endian encoded, +with bits assigned to R, G and B components as follows (a.k.a. RGB565): + + * bit 15-11: red + * bit 10- 5: green + * bit 4 - 0: blue + +Entropy coded data + +Following the thumbnail image (if present), is the entropy coded data. This +data looks a bit like the entropy coded data used in JPEG images. + + +The image is sub-divided as follows: + + * The image is divided in vertical strips of 16 pixels wide and height + pixels high, so the whole image consists of width/16 strips, encoded + from left-to-right. + * Each strip is subdivided in MCUs (minimum coded unit) of 16x16 pixels, + encoded from top-to-bottom. + * Each MCU consists of 4 blocks of 8x8 pixels each, containing pixels +from the Bayer mosaicing pattern. They appear in the following order: + o First the green pixels of the top half of the 16x16 pixel MCU + o Then the green pixels of the bottom half of the 16x16 pixel MCU + o Then the red pixels for the entire MCU + o Finally the blue pixels for the entire MCU + * The order of the red, green and blue pixels in the Bayer pattern is as + follows: RG/GB. This means the first line of a MCU starts with a red + pixel, then a green pixel from the first green line from the first + green block. The second line starts with a green pixel from the second + green line from the first green block, then a blue pixel. + + +With respect to the encoding: + + * MCUs in a strip are encoded like a JPEG MCU. There are only two + huffman tables (one for the DC and for the AC coefficients) and there + is only one quantisation table. + * The blue and red 8x8 blocks inside an MCU each have a DC coefficient + that is differential to the DC coefficient of the same block in the + previous MCU, like in standard JPEG. + * The two green 8x8 blocks inside an MCU share a single DC coefficient, + so the DC coefficient of the first green block is differential to the + one from the second block and vice versa. + * The DC coefficients in the first MCU in a strip are not differential, + but contain the absolute value. + * 0xFF bytes in the entropy coded data are followed by 0x00 stuff-bytes, +just like they are in JPEG. + * A strip is encoded as a bunch of MCUs, followed by 0xFFD9 (this is the +End-Of-Image marker in JPEG) and padded with 0x00 until the next 16-byte + aligned address. The start of the entropy coded stream is considered +to be address 0 in this case. + * An image is encoded as a bunch of strips, followed by bogus data until +the end of the total image data (which is a multiple of 0x80 or 0x200). + +Huffman table + +The Huffman table used is the recommended table for the luminance component +from the JPEG specification. In C, encoded as a JPEG DHT table, this looks +like: + +static const unsigned char dht_table[] = { + 0xFF, 0xC4, + 0x00, 0xD2 + + // luma DC + 0x00, + 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, +0x0B, + + // luma AC + 0x10, + 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, +0x04, 0x00, 0x00, 0x01, 0x7D, + 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, +0x06, 0x13, 0x51, 0x61, 0x07, + 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, +0xC1, 0x15, 0x52, 0xD1, 0xF0, + 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, +0x1A, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, +0x45, 0x46, 0x47, 0x48, 0x49, + 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, +0x65, 0x66, 0x67, 0x68, 0x69, + 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, +0x85, 0x86, 0x87, 0x88, 0x89, + 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, +0xA3, 0xA4, 0xA5, 0xA6, 0xA7, + 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, +0xBA, 0xC2, 0xC3, 0xC4, 0xC5, + 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, +0xD8, 0xD9, 0xDA, 0xE1, 0xE2, + 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, +0xF4, 0xF5, 0xF6, 0xF7, 0xF8, + 0xF9, 0xFA +}; + +Quantisation table + +The magnitude of the elements in the quantisation table in JPEG-like +encoding basically determines the compromise between image quality and image +size. A quantisation table with small values results in a high quality but +large image and a quantisation table with big values results in a low +quality but small image. + +The quantisation table used in the Jeilin encoding is a scaled version of a +base quantisation table. This base quantisation table is very similar +(within a few percent) to the one given in the JPEG specification. + +The scale factor (in percents) is calculated from the quality value from the +header (call it q) in the following way: + + * q = 0: scale factor = 5000 + * 0 < q <= 50: scale factor = 5000 / q + * 50 < q <= 100: scale factor = 2 * (100 - q) + * q > 100: assume q = 100 + +Scaling is done by multiplying each element of the table by the scale +factor, dividing by 100 and rounding to the nearest integer value. The +minimum value of an element after scaling is 1 and the maximum value is 255. +Retrieved from +"http://sourceforge.net/apps/mediawiki/gphoto/index.php?title=ImageEncodingJeilin2005CD" +Views + + * Page + * Discussion + * View source + * History + +Personal tools + +Navigation + + * Main Page + * Community portal + * Current events + * Recent changes + * Random page + * Help + +Search + +Toolbox + + * What links here + * Related changes + * Special pages + * Printable version + * Permanent link + +Powered by MediaWiki + + * This page was last modified on 7 April 2010, at 00:05. + * This page has been accessed 167 times. + + +© 2010 Geeknet, Inc. Terms of Use Privacy Policy + diff --git a/camlibs/jl2005c/README.jl2005c b/camlibs/jl2005c/README.jl2005c index 01d71f2ea..60e58727e 100644 --- a/camlibs/jl2005c/README.jl2005c +++ b/camlibs/jl2005c/README.jl2005c @@ -1,192 +1,144 @@ JEILIN STILLCAM DRIVER + Copyright Theodore Kilgore <kilgota@auburn.edu> September 4, 2007. Most recent -update is April 16, 2009. +update is April 19, 2010. -(Everything in libgphoto2/camlibs/jeilin is LGPL-licensed, including this -README. See any of the source files for a more complete statement of the +(Everything in libgphoto2/camlibs/jeilin is LGPL-licensed, including this +README. See any of the source files for a more complete statement of the license.) INTRODUCTION -This driver is intended to support cameras containing the JL2005C chip from -Jeilin Technologies and also the similar cameras which have in them the -JL2005B or the JL2005D. The interface is proprietary, and these cameras are -supported commercially only in Windows. Jeilin Technologies also manufactures -chips which go into mass storage cameras. Those cameras, not to be confused -with these, can be accessed directly using mass storage support. The company -also makes another camera controller chip with proprietary interface, the -JL2005A. Cameras with that chip are supported in libgphoto2/camlibs/jl2005a. -Those cameras are much better supported than are the JL2005B/C/D cameras. But -if you want to read about one of those JL2005A cameras, please look in -camlibs/jl2005a, not here. - -THIS IS EXPERIMENTAL CODE - -This code in this directory is included in the source release of libgphoto2, -from version 2.4.4 onward. However, the code in this directory does not get -compiled and installed by default. There is a reason for that: - -THIS IS EXPERIMENTAL CODE - -With the libgphoto2 source tree in hand, you have to follow the instructions -in INSTALL to compile and install it. If you have never done this procedure, -then perhaps you would be very well advised to try to do this first without -attempting to install support for JL2005C cameras. The code in -libgphoto2/camlibs/jl2005c does not compile by default if you follow only the -standard procedures, as I said, so do not expect it to be compiled if you did -not do anything special. - -If you really want to compile and install the support for the JL2005C cameras, -then there are at least two methods: - -1. At the ./configure step during the compilation of the main libgphoto2 tree, -the option - -./configure --with-camlibs=all,jl2005c - -should to "turn on" the camlib (note that "all" repeats the default option, -which explicitly excludes the jl2005c so you must explicitly add it here). -Then you can proceed as normally by doing make, then make install, and the -JL2005C support will be made and installed with all the rest. - -An alternative to the above is - -2. If you have already installed the entire libgphoto2 (except for the jl2005c -library) you can go into libgphoto2/camlibs and (as root or su) from there do - -make CAMLIBS="jl2005c.la" install-camlibs - -Then, this subdirectory will be compiled and installed with the others. - -Once you have installed support for the jl2005c you should be ready to try out -the camera and to help me to figure out a decent decompression algorithm. For, -the camera uses data compression, and the method of the compression is unknown. - -Oh, if you are not experienced in libgphoto2 type things, you will notice that -at this point you can only use this camera as root. To solve that problem, -follow the instructions in libgphoto2/packaging/generic if you are using udev -(usually associated with using a recent 2.6.x Linux kernel), or if you know -you are using hotplugging then follow the instructions in -libgphoto2/packaging/linux-hotplug (probably you use hotplugging only if you -are still using a 2.4.x Linux kernel). You can either set up a group called -"camera" or let all users use the camera. Me, I let all users use all cameras -hooked to my home system, because "all users" pretty much consists of me, -myself, and I. - -As an alternative to what is in the previous paragraph, you can just go to the -directory /etc/udev/rules.d and edit the existing rules file for libgphoto by -adding a line for the new camera. To do this, just copy a line which relates to -some other camera, and change the USB vendor and product numbers to match. - +This driver is intended to support the cameras with the USB Vendor:Product +number 0x0979:0x0227. These cameras all have a controller chip from Jeilin +Technologies, either the JL2005B or the JL2005C or the JL2005D. Both the +control interface and the compressed data format of these cameras are +proprietary, and these cameras are supported commercially only in Windows. +In spite of some features in common, these cameras also show a very large +variation. Some of them are very rudimentary, with a maximum resolution +of 352x288, and there are also more expensive models with a maximum resolution +of 1280x1024. Some of them have viewing screens on the rear and some +do not. Some of the cameras also produce separate thumbnails, which, when +present, are downloaded as part of the raw data for the image. Accommodation +of this very wide range of features has been a special challenge in writing +this support library, especially when it seems that not all of the OEM +software available for a given supported camera will work for all the others. + +Jeilin Technologies also manufactures chips which go into mass storage cameras. Those cameras, not to be confused +with these, can be accessed directly using mass storage support. The company +also makes another camera controller chip with proprietary interface, the +JL2005A. Cameras with that chip are supported in libgphoto2/camlibs/jl2005a. +If you want to read about one of those JL2005A cameras, please look in +camlibs/jl2005a, not here. + +The JL2005C camera library has been included in the release versions of +libgphoto2 ever since libgphoto2-2.4.4, but it was so configured as not +to compile by default. The reason for that was that the cameras use a nasty +data compression algorithm which was very difficult to figure out. So long +as the data coming out of the camera could not be decompressed, it was not +of any use to support these cameras except for experimental purposes. This +situation has recently changed. A task force was set up to deal with the +problem, and success has been achieved. As of April 2010, it seems to be +possible reliably to get the photos out of these cameras in usable form. +For details concerning the solution of the decompression problem, please +see the file README.jl2005bcd-compression which is supposed to be included +with the documentation for libgphoto2/camlibs/jl2005c. If for any reason +you do not have this file, it can be found at <gphoto.svn.sourceforge.net> +in the directory trunk/libgphoto2/camlibs/jl2005c. My thanks to the other +members of the team who worked together to solve the longstanding problem. +Thanks to Adam Rubin and Bertrik Sikken for doing the research needed to +gather the information found at + +<http://sourceforge.net/apps/mediawiki/gphoto/index.php?title=ImageEncoding +Jeilin2005CD> + +And thanks to Hans de Goede for writing the decompression algorithm. WHAT DOES THIS CAMERA LIBRARY CURRENTLY DO? -It will hook up the camera, and you ought to be able to download a dump of any -photo data in it using the command gphoto2 --get-all-raw-data. The main reason -you might want to do this right now is to participate in the development of -support for these cameras by experimenting with the still-unknown decompression -algorithm which they use. Help is welcome. That is why this library is made -public. Some other infrastructural work has been done, too. The code supports -the functioning of these cameras sufficiently well, that you ought to be able -to run gphoto --shell and it even ought to be possible to run a sequence like - -get-raw jl_026.ppm - -followed by the next gphoto2 shell command - -get-raw jl_001.ppm - -without corruption of data. - - -I _think_ that I got the data size for the photos all correct, but I am not -sure about that, for the obvious reason that I cannot do finished photos. -(Update 01/05/08: It has been verified for the JL2005D, the raw image data -obtained by the OEM driver is the same as what this code is providing. I have -been unable to verify this for a JL2005B camera, also unable to verify for a -JL2005C). The camera does download without glitches all the photos in it, even -if it is full, and will save them as raw files. I have taken the trouble to -make each raw file slightly longer by adding the 16-byte line related to it -which is found in the photo allocation table, the first block of data to be -downloaded from the camera. - - -The reason why one cannot make finished photos right now is that these -cameras use a proprietary compression algorithm which Jeilin Technologies is -currently not interested in sharing with us. Thus, the main purpose of -posting this driver is to publish the code so that further experimentation and -development can more easily take place. - - -!!!!!!!!!!!!!!! A SPECIAL WARNING !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -How to compute the size and location of a photo's raw data to be downloaded, -is probably correct now for all of these cameras. However, the basis for this -computation remains in the realm of guesswork. Be on the lookout for the -camera taking an inordinate amount of time to do some simple task such as to -count the number of photos in it using gphoto2 -n. That is probably a sign of -something previously gone wrong, which has jammed the camera. It may be needed -to replug it in order to clear the jam. - -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -ADDITIONAL REMARKS: - -These cameras use a proprietary compression algorithm, as already said. As a -result, progress in the development of this driver will inevitably be slow. - -Another challenge is that these cameras use an extremely primitive command -structure. The only thing that the camera knows how to do is to prepare to -download all data on it, download all data on it, and to reset prior to being -disconnected. Therefore, any other functionality has to be based on software -fakery which fools the hardware. Suppose, for example, that there are ten -photos in the camera. Then what the camera will do is: +This library appears to support the basic functionality of all the various +cameras which are available for testing. It seems to perform reliably at the +tasks of initializing the camera and providing a graceful exit, downloading +the raw data, and also conversion of raw data into decompressed output files. +Cameras which support or do not support thumbnails seem to be reliably +detected, and the raw data appears to be reliably decompressed. If present, +thumbnails can also be processed into a finished form. In short, things +appear basically to work. + +HARDWARE CONSTRAINTS: + +These cameras use an extremely primitive command structure. The only thing +that the camera really does know how to do is to prepare to download all data +on it, then to download all data on it, and then to reset prior to being +disconnected. Judging by what it does, the OEM software is almost certainly +built on the assumption that the camera will be initialized, have its data +dumped exactly once, and then disconnected. Everything else is done on the +computer with the big blob of data which has been downloaded. Therefore, +any other functionality has to be based on software fakery which fools the +hardware into emulating said functionality. Suppose, for example, that there +are ten photos in the camera. Then what the camera will do is: -- report the number of photos (ten) -- tell how much data needs to be downloaded in all (depends on how much room was taken up by the raw data for all the photos) -- provide a table with one line for each photo, giving its size, start location, output height and width, and some kind of code for the compression method used on it. -In the next step, one must request all the image data to be downloaded, in -increments of which there is a maximum size. This does not need to be done -all at once, so it is possible to download some initial block of the data and -operate on whatever photos are already obtained, and then download another block. -However, ALL of the data has to be downloaded before the driver program exits. -One consequence of this is that the only way a command such as +In the next step, one must request all the image data for all the ten photos +to be downloaded. This happens in increments of 64000 bytes, with only the last +increment permitted to be "short." This does not need to be done all at once; +it is possible to download some initial block of the data and operate on +whatever photos are already obtained, keeping anything left over until the next +block is downloaded. However, ALL of the data must downloaded before the driver + program exits. One consequence of this is that the only way commands such as -gphoto2 --get-raw-data 3 +gphoto2 --get-raw-data 3 or gphoto2 -p 3 can work is to download photos number 1 and 2 and throw away the data before -downloading the desired number 3 which is to be kept as a raw file, -and then downloading any data in the camera which comes after photo number 3 -after that, as a standard part of the exit routine. Another consequence is -that the sequence of downloading number 10, say, followed by number 1 is even -more complicated. The actual steps required are: +downloading the desired number 3 which is to be kept, then downloading any +data in the camera which comes after photo number 3, as part of the exit +routine. The sequence of downloading number 10, say, followed by number 1 is +even more complicated. The actual steps required are: -- initialize the camera -- download enough data to get photo number 10, throwing away all data which is not part of photo number 10 but precedes it -- download the data required for photo number 10 - -- download the rest of the data + -- download the rest of the data, if any -- send a reset sequence to the camera -- close and reopen the camera's port - -- run a "rewind" function consisting of a fake repetition of the - initialization sequence, including downloading again the allocation table + -- repeat the initialization sequence -- download enough data to process photo number 1 - -- download all the rest of the data and throw it away. + -- download all the rest of the data and throw it away. -- send the reset sequence again, and, finally, disconnect -Finally, if and when the decompression problem is resolved, then it ought to -become possible to use other programs with this camera, such as a GUI frontend -for libgphoto2. Any such program used with this camera must be willing to call -and actually to use the camera_exit() function which is contained in this -library, else, given the above constraints, it is obvious that the camera would +In addition to the awkwardness caused by this kind of primitive behavior, it +puts certain requirements on any program used to handle the camera. Most +particularly, any program which uses libgphoto2 must be willing to call and +actually to use the camera_exit() function which is contained in this library. +Otherwise, given the above constraints, it is obvious that the camera would not work properly with that frontend program. +TO DO + +There are some parts of the camera command structure which are not completely +known at this time. For example, the only way known to delete photos on these +cameras is to use combinations of button pushes on the camera, or to remove +the batteries (the cameras run on volatile SDRAM, like many other cheap +cameras). It could be that some of the cameras might possibly support the +command gphoto2 -D (delete all) to be implemented. It appears unlikely, but +but the possibility is not completely investigated. Also, some kind of capture +function could conceivably be made to work. Right now, this is also unknown. + +One known functionality of the camera is not completely supported, though. The +cameras all support a "continuous shooting" mode, in which the camera shoots +frames until it it is stopped by a button press or runs out of memory. The OEM +driver software saves the result as an AVI file. Here, the individual frames +are merely downloaded and saved as individual photos. The intention is to +support this feature completely, in the future. WARRANTY? -Absolutely none. Remember, I did not sell you this software. I have written -this driver for my own edification and in the sincere hope that it might help -you to use of your camera. Please see also the warranty clauses +Absolutely none. Remember, I did not sell you this software. I have written +this driver for my own edification and in the sincere hope that it might help +you to use of your camera. Please see also the warranty clauses in the LGPL license. diff --git a/camlibs/jl2005c/img_enhance.c b/camlibs/jl2005c/img_enhance.c new file mode 100644 index 000000000..9caa98c94 --- /dev/null +++ b/camlibs/jl2005c/img_enhance.c @@ -0,0 +1,252 @@ +/* + * img_enhance.c + * + * Part of a processor program for raw data from JL2005B/C/D cameras. + * Based on previous work for several other cameras. + * + * Copyright (c) 2010 Theodore Kilgore <kilgota@auburn.edu> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * A previous version of the white_balance() function intended for use in + * libgphoto2/camlibs/aox is copyright (c) 2008 Amauri Magagna. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <string.h> +#include <math.h> +#include <gamma.h> +#include <bayer.h> +#include "img_enhance.h" + +#include <gphoto2/gphoto2.h> + +#define GP_MODULE "jl2005c" + +#ifndef CLIP +#define CLIP(x) ((x)<0?0:((x)>255)?255:(x)) +#endif + + +/* ===== White Balance / Color Enhance / Gamma adjust ===== + + Get histogram for each color plane + Expand to reach 0.5% of white dots in image + + Get new histogram for each color plane + Expand to reach 0.5% of black dots in image + + Get new histogram + Calculate and apply gamma correction + + If not a dark image: + For each dot, increase the color separation + + ========================================================== */ + +int +histogram (unsigned char *data, unsigned int size, int *htable_r, + int *htable_g, int *htable_b) +{ + int x; + /* Initializations */ + for (x = 0; x < 0x100; x++) { + htable_r[x] = 0; + htable_g[x] = 0; + htable_b[x] = 0; + } + /* Building the histograms */ + for (x = 0; x < (size * 3); x += 3) + { + + htable_r[data[x + 0]]++; /* red histogram */ + htable_g[data[x + 1]]++; /* green histogram */ + htable_b[data[x + 2]]++; /* blue histogram */ + } + return 0; +} + +int +white_balance (unsigned char *data, unsigned int size, float saturation) +{ + int x, r, g, b, max, d; + double r_factor, g_factor, b_factor, max_factor; + int htable_r[0x100], htable_g[0x100], htable_b[0x100]; + unsigned char gtable[0x100]; + double new_gamma, gamma = 1.0; + + /* ------------------- GAMMA CORRECTION ------------------- */ + + histogram(data, size, htable_r, htable_g, htable_b); + x = 1; + for (r = 64; r < 192; r++) + { + x += htable_r[r]; + x += htable_g[r]; + x += htable_b[r]; + } + new_gamma = sqrt((double) (x * 1.5) / (double) (size * 3)); + printf("Provisional gamma correction = %1.2f\n", new_gamma); + /* Recalculate saturation factor for later use. */ + saturation = saturation * new_gamma * new_gamma; + printf("saturation = %1.2f\n", saturation); + gamma = new_gamma; + if (new_gamma < .70) + gamma = 0.70; + if (new_gamma > 1.2) + gamma = 1.2; + printf("Gamma correction = %1.2f\n", gamma); + gp_gamma_fill_table(gtable, gamma); + gp_gamma_correct_single(gtable, data, size); + if (saturation < .5 ) /* If so, exit now. */ + return 0; + + /* ---------------- BRIGHT DOTS ------------------- */ + max = size / 200; + histogram(data, size, htable_r, htable_g, htable_b); + + for (r = 0xfe, x = 0; (r > 32) && (x < max); r--) + x += htable_r[r]; + for (g = 0xfe, x = 0; (g > 32) && (x < max); g--) + x += htable_g[g]; + for (b = 0xfe, x = 0; (b > 32) && (x < max); b--) + x += htable_b[b]; + r_factor = (double) 0xfd / r; + g_factor = (double) 0xfd / g; + b_factor = (double) 0xfd / b; + + max_factor = r_factor; + if (g_factor > max_factor) max_factor = g_factor; + if (b_factor > max_factor) max_factor = b_factor; + if (max_factor >= 4.0) { + /* + * We need a little bit of control, here. If max_factor is big + * then the photo was very dark, after all. + */ + if (2.0 * b_factor < max_factor) + b_factor = max_factor / 2.; + if (2.0 * r_factor < max_factor) + r_factor = max_factor / 2.; + if (2.0 * g_factor < max_factor) + g_factor = max_factor/2.; + r_factor = (r_factor / max_factor) * 4.0; + g_factor = (g_factor / max_factor) * 4.0; + b_factor = (b_factor / max_factor) * 4.0; + } + + if (max_factor > 1.5) + saturation = 0; + printf("White balance (bright): "); + printf("r=%1d, g=%1d, b=%1d, fr=%1.3f, fg=%1.3f, fb=%1.3f\n", + r, g, b, r_factor, g_factor, b_factor); + if (max_factor <= 1.4) { + for (x = 0; x < (size * 3); x += 3) + { + d = (data[x + 0] << 8) * r_factor + 8; + d >>= 8; + if (d > 0xff) + d = 0xff; + data[x + 0] = d; + d = (data[x + 1] << 8) * g_factor + 8; + d >>= 8; + if (d > 0xff) + d = 0xff; + data[x + 1] = d; + d = (data[x + 2] << 8) * b_factor + 8; + d >>= 8; + if (d > 0xff) + d = 0xff; + data[x + 2] = d; + } + } + /* ---------------- DARK DOTS ------------------- */ + max = size / 200; /* 1/200 = 0.5% */ + histogram(data, size, htable_r, htable_g, htable_b); + + for (r = 0, x = 0; (r < 96) && (x < max); r++) + x += htable_r[r]; + for (g = 0, x = 0; (g < 96) && (x < max); g++) + x += htable_g[g]; + for (b = 0, x = 0; (b < 96) && (x < max); b++) + x += htable_b[b]; + + r_factor = (double) 0xfe / (0xff - r); + g_factor = (double) 0xfe / (0xff - g); + b_factor = (double) 0xfe / (0xff - b); + + printf("White balance (dark): "); + printf("r=%1d, g=%1d, b=%1d, fr=%1.3f, fg=%1.3f, fb=%1.3f\n", + r, g, b, r_factor, g_factor, b_factor); + + for (x = 0; x < (size * 3); x += 3) + { + d = (int) 0xff08 - (((0xff - data[x + 0]) << 8) * r_factor); + d >>= 8; + if (d < 0) + d = 0; + data[x + 0] = d; + d = (int) 0xff08 - (((0xff - data[x + 1]) << 8) * g_factor); + d >>= 8; + if (d < 0) + d = 0; + data[x + 1] = d; + d = (int) 0xff08 - (((0xff - data[x + 2]) << 8) * b_factor); + d >>= 8; + if (d < 0) + d = 0; + data[x + 2] = d; + } + + /* ------------------ COLOR ENHANCE ------------------ */ + + if(saturation > 0.0) { + for (x = 0; x < (size * 3); x += 3) + { + r = data[x + 0]; g = data[x + 1]; b = data[x + 2]; + d = (int) (r + g + b) / 3.; + if ( r > d ) + r = r + (int) ((r - d) + * (0xff - r) / (0x100 - d) + * saturation); + else + r = r + (int) ((r - d) + * (0xff - d) / (0x100 - r) + * saturation); + if (g > d) + g = g + (int) ((g - d) + * (0xff - g) / (0x100 - d) + * saturation); + else + g = g + (int) ((g - d) + * (0xff - d) / (0x100 - g) + * saturation); + if (b > d) + b = b + (int) ((b - d) + * (0xff - b) / (0x100 - d) + * saturation); + else + b = b + (int) ((b - d) + * (0xff - d) / (0x100 - b) + * saturation); + data[x + 0] = CLIP(r); + data[x + 1] = CLIP(g); + data[x + 2] = CLIP(b); + } + } + return 0; +} diff --git a/camlibs/jl2005c/img_enhance.h b/camlibs/jl2005c/img_enhance.h new file mode 100644 index 000000000..0f0a5938c --- /dev/null +++ b/camlibs/jl2005c/img_enhance.h @@ -0,0 +1,35 @@ +/* + * img_enhance.h + * + * Header file for image processor for raw files from JL2005B/C/D + * cameras. + * + * Copyright (c) 2010 Theodore Kilgore <kilgota@auburn.edu> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __IMG_ENHANCE_H__ +#define __IMG_ENHANCE_H__ + + +int +histogram(unsigned char *data, unsigned int size, int *htable_r, + int *htable_g, int *htable_b); +int +white_balance(unsigned char *data, unsigned int size, float saturation); + +#endif diff --git a/camlibs/jl2005c/jl2005bcd_decompress.c b/camlibs/jl2005c/jl2005bcd_decompress.c new file mode 100644 index 000000000..37444537c --- /dev/null +++ b/camlibs/jl2005c/jl2005bcd_decompress.c @@ -0,0 +1,280 @@ +/* decompress.c + * + * Converts raw output from Jeilin JL2005B/C/D into PPM files. + * + * The jl2005bcd_raw_converter is + * Copyright (c) 2010 Theodore Kilgore <kilgota@auburn.edu> + * + * The decompression code used is + * Copyright (c) 2010 Hans de Goede <hdegoede@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <netinet/in.h> + +#include "jl2005bcd_decompress.h" +#include "jpeg_memsrcdest.c" +#include <bayer.h> +#include "img_enhance.h" +#include <math.h> + +#include <gphoto2/gphoto2.h> +#include <gphoto2/gphoto2-port.h> + +#define GP_MODULE "jl2005c" + + +#define JPEG_HEADER_SIZE 338 +#define JPEG_HEIGHT_OFFSET 94 + +static int +find_eoi (uint8_t *jpeg_data, int jpeg_data_idx, int jpeg_data_size) +{ + int i; + + for (i = jpeg_data_idx; i < (jpeg_data_size - 1); i++) + if (jpeg_data[i] == 0xff && jpeg_data[i + 1] == 0xd9) + break; + + if (i >= (jpeg_data_size - 1)) { + printf("AAI\n"); + return -1; + } + + return i + 2; /* + 2 -> Point to after EOI marker */ +} + +int +jl2005bcd_decompress (unsigned char *output, unsigned char *input, + int inputsize, int get_thumbnail) +{ + int out_headerlen; + uint16_t *thumb = NULL; + unsigned char *header; + uint8_t jpeg_stripe[500000]; + uint8_t out[5000000]; + unsigned char *jpeg_data; + int q, width, height; + int thumbnail_width, thumbnail_height; + struct jpeg_compress_struct cinfo; + struct jpeg_decompress_struct dinfo; + struct jpeg_error_mgr jcerr, jderr; + JOCTET *jpeg_header = NULL; + int outputsize = 0; + unsigned long jpeg_header_size = 0; + int i, x, y, x1, y1, jpeg_data_size, jpeg_data_idx, eoi, size, ret; + + + GP_DEBUG("Running jl2005bcd_decompress() function.\n"); + + header = input; + q = header[3] & 0x7f; + height = header[4] * 8; + width = header[5] * 8; + printf("quality is %d\n", q); + printf("size: %dx%d\n", width, height); + switch (header[9] & 0xf0) { + case 0xf0: + thumbnail_width = 128; + thumbnail_height = 120; + break; + case 0x60: + thumbnail_width = 96; + thumbnail_height = 64; + break; + default: + thumbnail_width = 0; + thumbnail_height = 0; + } + if (header[1] & 3) + thumbnail_width = 0; + if (get_thumbnail) { + if (!thumbnail_width) { + GP_DEBUG("No thumbnail is present!\n"); + return GP_ERROR_NOT_SUPPORTED; + } else { + thumb = input + 16; + for (i = 0; i < thumbnail_width * thumbnail_height; + i++) { + thumb[i] = ntohs(thumb[i]); + out[i * 3 + 0] = (thumb[i] & 0xf800) >> 8; + out[i * 3 + 1] = (thumb[i] & 0x07e0) >> 3; + out[i * 3 + 2] = (thumb[i] & 0x001f) << 3; + } + + out_headerlen = snprintf((char *)output, 256, + "P6\n" + "# CREATOR: gphoto2, JL2005BCD library\n" + "%d %d\n" + "255\n", + thumbnail_width, + thumbnail_height); + white_balance (out, thumbnail_width * thumbnail_height, + 1.6); + memcpy(output + out_headerlen, out, + thumbnail_width * thumbnail_height * 3); + outputsize = thumbnail_width * thumbnail_height * 3 + + out_headerlen; + return outputsize; + } + } + /* + * And the fun begins, first of all create a dummy jpeg, which we use + * to get the headers from to feed to libjpeg when decompressing the + * stripes. This way we can use libjpeg's quant table handling + * (and built in default huffman tables). + */ + cinfo.err = jpeg_std_error (&jcerr); + jpeg_create_compress (&cinfo); + jpeg_mem_dest (&cinfo, &jpeg_header, &jpeg_header_size); + cinfo.image_width = 16; + cinfo.image_height = 16; + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + jpeg_set_defaults (&cinfo); + /* Make comp[0] (which will be green) 1x2 subsampled */ + cinfo.comp_info[0].h_samp_factor = 1; + cinfo.comp_info[0].v_samp_factor = 2; + /* Make comp[1] and [2] use huffman table and quanttable 0, as all + * components use luminance settings with the jl2005b/c/d */ + cinfo.comp_info[1].quant_tbl_no = 0; + cinfo.comp_info[1].dc_tbl_no = 0; + cinfo.comp_info[1].ac_tbl_no = 0; + cinfo.comp_info[2].quant_tbl_no = 0; + cinfo.comp_info[2].dc_tbl_no = 0; + cinfo.comp_info[2].ac_tbl_no = 0; + /* Apply the quality setting from the header */ + if (q <= 0) + i = 5000; + else if (q <= 50) + i = 5000 / q; + else if (q <= 100) + i = 2 * (100 - q); + else + i = 0; + jpeg_set_linear_quality(&cinfo, i, TRUE); + + jpeg_start_compress (&cinfo, TRUE); + while( cinfo.next_scanline < cinfo.image_height ) { + JOCTET row[16 * 3]; + JSAMPROW row_pointer[1] = { row }; + jpeg_write_scanlines (&cinfo, row_pointer, 1); + } + jpeg_finish_compress (&cinfo); + jpeg_destroy_compress (&cinfo); + + JSAMPLE green[8 * 16]; + JSAMPLE red[8 * 8]; + JSAMPLE blue[8 * 8]; + JSAMPROW green_row_pointer[16]; + JSAMPROW red_row_pointer[8]; + JSAMPROW blue_row_pointer[8]; + + for (i = 0; i < 16; i++) + green_row_pointer[i] = green + i * 8; + + for (i = 0; i < 8; i++) { + red_row_pointer[i] = red + i * 8; + blue_row_pointer[i] = blue + i * 8; + } + + JSAMPARRAY samp_image[3] = { green_row_pointer, + red_row_pointer, + blue_row_pointer }; + + memcpy(jpeg_stripe, jpeg_header, JPEG_HEADER_SIZE); + jpeg_stripe[JPEG_HEIGHT_OFFSET ] = height >> 8; + jpeg_stripe[JPEG_HEIGHT_OFFSET + 1] = height; + jpeg_stripe[JPEG_HEIGHT_OFFSET + 2] = 0; + jpeg_stripe[JPEG_HEIGHT_OFFSET + 3] = 8; + free (jpeg_header); + jpeg_data = input + 16 + 2 * thumbnail_width * thumbnail_height; + jpeg_data_size = inputsize - 16 + - 2 * thumbnail_width * thumbnail_height; + + jpeg_data_idx = 0; + + memset(out, 0, width * height * 3); + + dinfo.err = jpeg_std_error (&jderr); + jpeg_create_decompress (&dinfo); + for (x = 0; x < width; x += 16) { + eoi = find_eoi(jpeg_data, jpeg_data_idx, jpeg_data_size); + if (eoi < 0) + return eoi; + + size = eoi - jpeg_data_idx; + if ((JPEG_HEADER_SIZE + size) > sizeof(jpeg_stripe)) { + printf("AAAIIIIII\n"); + return 1; + } + memcpy (jpeg_stripe + JPEG_HEADER_SIZE, + jpeg_data + jpeg_data_idx, size); + + jpeg_mem_src (&dinfo, jpeg_stripe, JPEG_HEADER_SIZE + size); + jpeg_read_header (&dinfo, TRUE); + dinfo.raw_data_out = TRUE; +#if JPEG_LIB_VERSION >= 70 + dinfo.do_fancy_upsampling = FALSE; +#endif + jpeg_start_decompress (&dinfo); + for (y = 0; y < height; y += 16) { + jpeg_read_raw_data (&dinfo, samp_image, 16); + for (y1 = 0; y1 < 16; y1 += 2) { + for (x1 = 0; x1 < 16; x1 += 2) { + out[((y + y1 + 0) * width + + x + x1 + 0) * 3] + = red[y1 * 4 + x1 / 2]; + out[((y + y1 + 0) * width + + x + x1 + 1) * 3 + 1] + = green[y1 * 8 + x1 / 2]; + out[((y + y1 + 1) * width + + x + x1 + 0) * 3 + 1] + = green[y1 * 8 + 8 + x1 / 2]; + out[((y + y1 + 1) * width + + x + x1 + 1) * 3 + 2] + = blue[y1 * 4 + x1 / 2]; + } + } + } + jpeg_finish_decompress (&dinfo); + + /* Set jpeg_data_idx for the next stripe */ + jpeg_data_idx = (jpeg_data_idx + size + 0x0f) & ~0x0f; + } + jpeg_destroy_decompress(&dinfo); + + ret = gp_ahd_interpolate(out, width, height, BAYER_TILE_BGGR); + if (ret < 0) { + printf("HEUH?\n"); + return ret; + } + white_balance (out, width*height, 1.6); + + out_headerlen = snprintf((char *)output, 256, + "P6\n" + "# CREATOR: gphoto2, JL2005BCD library\n" + "%d %d\n255\n", + width, + height); + GP_DEBUG("out_headerlen = %d\n", out_headerlen); + memcpy(output + out_headerlen, out, width * height * 3); + outputsize = out_headerlen + width * height * 3; + return outputsize; +} diff --git a/camlibs/jl2005c/jl2005bcd_decompress.h b/camlibs/jl2005c/jl2005bcd_decompress.h new file mode 100644 index 000000000..e0c4ac777 --- /dev/null +++ b/camlibs/jl2005c/jl2005bcd_decompress.h @@ -0,0 +1,30 @@ +/* jl2005bcd_decompress.h + * + * Copyright (C) 2006-2010 Theodore Kilgore <kilgota@auburn.edu> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __jl2005bcd_decompress_H__ +#define __jl2005bcd_decompress_H__ + + +int +jl2005bcd_decompress(unsigned char *output, unsigned char *input, + int inputsize, int get_thumbnail); + +#endif + diff --git a/camlibs/jl2005c/jl2005c.c b/camlibs/jl2005c/jl2005c.c index b620a10ce..cb908ea26 100644 --- a/camlibs/jl2005c/jl2005c.c +++ b/camlibs/jl2005c/jl2005c.c @@ -1,16 +1,16 @@ /* jl2005c.c * - * Copyright (C) 2006 Theodore Kilgore <kilgota@auburn.edu> + * Copyright (C) 2006-2010 Theodore Kilgore <kilgota@auburn.edu> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. + * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the @@ -32,307 +32,226 @@ #include "jl2005c.h" -#define GP_MODULE "jl2005c" +#define GP_MODULE "jl2005c" -int -jl2005c_init (Camera *camera, GPPort *port, CameraPrivateLibrary *priv) +int +jl2005c_init (Camera *camera, GPPort *port, CameraPrivateLibrary *priv) { - unsigned char command[2]; char response; - char model_string[4]; - unsigned char info[0xe000]; - int info_block_size = 0; - memset(info,0, sizeof(info)); - memset(command,0,sizeof(command)); + int model_string = 0; + /* Needs to be big enough to hold (0xfff + 3) * 0x10 */ + unsigned char info[0x10020]; + const char camera_id[] = {0x4a, 0x4c, 0x32, 0x30, 0x30, 0x35}; + int alloc_table_size; + int attempts = 0; +restart: + alloc_table_size = 0; + memset(info, 0, sizeof(info)); GP_DEBUG("Running jl2005c_init\n"); - set_usb_in_endpoint (camera, 0x84); - gp_port_write (port, "\x08\x00", 2); - usleep (10000); - gp_port_write (port, "\x95\x60", 2); - usleep (10000); - gp_port_read (port, &response, 1); - model_string[0]=response; - usleep (10000); - gp_port_write (port, "\x95\x61", 2); - usleep (10000); - gp_port_read (port, &response, 1); - model_string[1]=response; - usleep (10000); - gp_port_write (port, "\x95\x62", 2); - usleep (10000); - gp_port_read (port, &response, 1); - model_string[2]=response; - usleep (10000); - gp_port_write (port,"\x95\x63" , 2); - usleep (10000); -gp_port_read (port, &response, 1); - model_string[3]=response; - GP_DEBUG("Model string is %02x%02x%02x%02x\n",model_string[0], model_string[1], - model_string[2], model_string[3]); - usleep (10000); - gp_port_write (port, "\x95\x64", 2); - usleep (10000); - gp_port_read (port, &response, 1); - - usleep (10000); - gp_port_write (port, "\x95\x65", 2); - usleep (10000); - gp_port_read (port, &response, 1); /* Number of pix returned here */ - priv->nb_entries = (unsigned)response; - GP_DEBUG("%d entries in the camera\n", response); - GP_DEBUG("%d entries in the camera\n", priv->nb_entries); - info_block_size = ((unsigned)response * 0x10) + 2; - if (info_block_size%0x200) - info_block_size += 0x200 - (info_block_size%0x200); - usleep (10000); + if (priv->init_done) { + gp_port_close(port); + usleep (100000); + gp_port_open(port); + } - gp_port_write (port, "\x95\x66", 2); - usleep (10000); - gp_port_read (port, &response, 1); - usleep (10000); - gp_port_write (port, "\x95\x67", 2); - usleep (10000); - gp_port_read (port, &response, 1); + set_usb_in_endpoint (camera, 0x84); + gp_port_write (port, "\x08\x00", 2); usleep (10000); - - gp_port_write (port, "\x95\x68", 2); + gp_port_write (port, "\x95\x60", 2); usleep (10000); gp_port_read (port, &response, 1); + model_string = response; usleep (10000); - - gp_port_write (port, "\x95\x69", 2); + gp_port_write (port, "\x95\x61", 2); usleep (10000); gp_port_read (port, &response, 1); + model_string += (response & 0xff) << 8; usleep (10000); - - gp_port_write (port, "\x95\x6a", 2); + gp_port_write (port, "\x95\x62", 2); usleep (10000); gp_port_read (port, &response, 1); + model_string += (response & 0xff) << 16; usleep (10000); - - gp_port_write (port, "\x95\x6b", 2); + gp_port_write (port,"\x95\x63" , 2); usleep (10000); gp_port_read (port, &response, 1); + model_string += (response & 0xff) << 24; + GP_DEBUG("Model string is %08x\n", model_string); usleep (10000); - - gp_port_write (port, "\x95\x6c", 2); + gp_port_write (port, "\x95\x64", 2); usleep (10000); gp_port_read (port, &response, 1); - priv->data_to_read = (response &0xff)*0x100; - - gp_port_write (port, "\x95\x6d", 2); - usleep (10000); - gp_port_read (port, &response, 1); - usleep (10000); - - priv->data_to_read += (response&0xff); - priv->data_to_read *= 0x200; - priv->total_data_in_camera = priv->data_to_read; - GP_DEBUG ("data_to_read = 0x%lx = %lu\n", priv->data_to_read, - priv->data_to_read); - GP_DEBUG ("total_data_in_camera = 0x%lx = %lu\n", priv->data_to_read, - priv->data_to_read); - - gp_port_write (port, "\x95\x6e", 2); - usleep (10000); - gp_port_read (port, &response, 1); - - usleep (10000); - gp_port_write (port, "\x95\x6f", 2); - usleep (10000); - gp_port_read (port, &response, 1); - - usleep (10000); - gp_port_write (port, "\x0a\x00", 2); - - usleep (10000); - /* Switch the inep over to 0x82. It stays there ever after. */ - set_usb_in_endpoint (camera, 0x82); - usleep (10000); - gp_port_read(port, (char *)info, info_block_size); - usleep (10000); - gp_port_write (port, "\x0b\x00",2); - usleep (10000); - memmove(priv->info, info, info_block_size); - priv->model=info[6]; - - GP_DEBUG("Leaving jl2005c_init\n"); - return GP_OK; -} - - -int -jl2005c_rewind (Camera *camera, GPPort *port) -{ -// gp_port_write (port, "\x0b\x00",2); - unsigned char command[2]; - char response; - unsigned char info[0xe000]; - int info_block_size = 0; - int junk_to_read = 0; - memset(info,0, sizeof(info)); - memset(command,0,sizeof(command)); - GP_DEBUG("Running jl2005c_rewind\n"); - gp_port_close(port); - usleep (100000); - gp_port_open(port); - - set_usb_in_endpoint (camera, 0x84); - gp_port_write (port, "\x08\x00", 2); - usleep (10000); - gp_port_write (port, "\x95\x60", 2); - usleep (10000); - gp_port_read (port, &response, 1); - usleep (10000); - gp_port_write (port, "\x95\x61", 2); - usleep (10000); - gp_port_read (port, &response, 1); - usleep (10000); - gp_port_write (port, "\x95\x62", 2); - usleep (10000); - gp_port_read (port, &response, 1); - usleep (10000); - gp_port_write (port,"\x95\x63" , 2); - usleep (10000); - gp_port_read (port, &response, 1); - usleep (10000); - gp_port_write (port, "\x95\x64", 2); - usleep (10000); - gp_port_read (port, &response, 1); - - usleep (10000); gp_port_write (port, "\x95\x65", 2); usleep (10000); gp_port_read (port, &response, 1); - info_block_size = ((unsigned)response * 0x10) + 2; - if (info_block_size%0x200) - info_block_size += 0x200 - (info_block_size%0x200); + /* Number of pix returned here, but not reliably reported */ + priv->nb_entries = response & 0xff; + GP_DEBUG("%d frames in the camera (unreliable!)\n", priv->nb_entries); usleep (10000); gp_port_write (port, "\x95\x66", 2); usleep (10000); - gp_port_read (port, &response, 1); + gp_port_read (port, &response, 1); usleep (10000); - gp_port_write (port, "\x95\x67", 2); + gp_port_write (port, "\x95\x67", 2); usleep (10000); gp_port_read (port, &response, 1); usleep (10000); - gp_port_write (port, "\x95\x68", 2); + gp_port_write (port, "\x95\x68", 2); usleep (10000); gp_port_read (port, &response, 1); usleep (10000); - gp_port_write (port, "\x95\x69", 2); + gp_port_write (port, "\x95\x69", 2); usleep (10000); gp_port_read (port, &response, 1); usleep (10000); - gp_port_write (port, "\x95\x6a", 2); + gp_port_write (port, "\x95\x6a", 2); usleep (10000); gp_port_read (port, &response, 1); usleep (10000); - gp_port_write (port, "\x95\x6b", 2); + gp_port_write (port, "\x95\x6b", 2); usleep (10000); gp_port_read (port, &response, 1); usleep (10000); - gp_port_write (port, "\x95\x6c", 2); + gp_port_write (port, "\x95\x6c", 2); usleep (10000); gp_port_read (port, &response, 1); - junk_to_read = (response &0xff)*0x100; + priv->data_to_read = (response & 0xff) * 0x100; - gp_port_write (port, "\x95\x6d", 2); + gp_port_write (port, "\x95\x6d", 2); usleep (10000); gp_port_read (port, &response, 1); usleep (10000); - junk_to_read += (response&0xff); - junk_to_read *= 0x200; - gp_port_write (port, "\x95\x6e", 2); + priv->data_to_read += (response&0xff); + priv->total_data_in_camera = priv->data_to_read; + GP_DEBUG ("blocks_to_read = 0x%lx = %lu\n", priv->data_to_read, + priv->data_to_read); + + gp_port_write (port, "\x95\x6e", 2); usleep (10000); gp_port_read (port, &response, 1); + alloc_table_size = (response & 0xff) * 0x200; + GP_DEBUG("alloc_table_size = 0x%02x * 0x200 = 0x%x\n", + response & 0xff, (response & 0xff) * 0x200); usleep (10000); - gp_port_write (port, "\x95\x6f", 2); + gp_port_write (port, "\x95\x6f", 2); usleep (10000); gp_port_read (port, &response, 1); usleep (10000); gp_port_write (port, "\x0a\x00", 2); - usleep (10000); + /* Switch the inep over to 0x82. It stays there ever after. */ + set_usb_in_endpoint (camera, 0x82); + usleep (10000); + /* Read the first block of the allocation table. */ + gp_port_read(port, (char *)info, 0x200); + if (strncmp(camera_id, (char*)info, 6)) { + GP_DEBUG("Error downloading alloc table\n"); + GP_DEBUG("Init attempted %d times\n", attempts + 1); + attempts++; + if (attempts == 3) { + GP_DEBUG("Third try. Giving up\n"); + return GP_ERROR; + } + goto restart; + } - /* Switch the inep over to 0x82. It stays there ever after. */ - set_usb_in_endpoint (camera, 0x82); - usleep (10000); - gp_port_read(port, (char *)info, info_block_size); - + /* Now check the number of photos. That is found in byte 13 of line 0 + * of the allocation table. + */ usleep (10000); + priv->nb_entries = (info[12] & 0xff) * 0x100 | (info[13] & 0xff); + GP_DEBUG("Number of entries is recalculated as %d\n", + priv->nb_entries); + + /* Just in case there was a problem, we now recalculate the total + * alloc_table_size. */ + alloc_table_size = priv->nb_entries * 0x10 + 0x30; + if (alloc_table_size%0x200) + alloc_table_size += 0x200 - (alloc_table_size%0x200); + /* However, we have already just now downloaded 0x200 bytes, so + * when downloading the rest of the table we correct for that and + * just download whatever remains of the information block. + */ + if (alloc_table_size > 0x200) + gp_port_read(port, (char *)info + 0x200, + alloc_table_size - 0x200); + memmove(priv->info, info, alloc_table_size); + priv->model=info[6]; + switch (priv->model) { + case 0x43: + case 0x44: + priv->blocksize = 0x200; + break; + case 0x42: + priv->blocksize = 0x80; + break; + default: + GP_DEBUG("Unknown model, unknown blocksize\n"); + return GP_ERROR_NOT_SUPPORTED; + } + GP_DEBUG("camera's blocksize = 0x%x = %d\n", priv->blocksize, + priv->blocksize); + /* Now a more responsible calculation of the amount of data in the + * camera, based upon the allocation table. */ + priv->data_to_read = info[10] * 0x100 | info[11]; + priv->data_to_read -= info[8] * 0x100 | info[9]; + priv->data_to_read *= priv->blocksize; + priv->total_data_in_camera = priv->data_to_read; + GP_DEBUG ("data_to_read = 0x%lx = %lu\n", priv->data_to_read, + priv->data_to_read); + GP_DEBUG ("total_data_in_camera = 0x%lx = %lu\n", priv->data_to_read, + priv->data_to_read); + priv->can_do_capture = 0; + if (info[7] & 0x04) + priv->can_do_capture = 1; gp_port_write (port, "\x0b\x00",2); usleep (10000); + priv->bytes_read_from_camera = 0; + priv->bytes_put_away = 0; - GP_DEBUG("Completing jl2005c_rewind\n"); - - return GP_OK; + priv->init_done = 1; + GP_DEBUG("Leaving jl2005c_init\n"); + return GP_OK; } - int jl2005c_get_pic_data_size (CameraPrivateLibrary *priv, Info *info, int n) { int size; - unsigned char model=priv->model; - GP_DEBUG("info[48+16*n+7] = %02X\n", info[48+16*n+7]); - size = info[0x30+0x10*n+6]*0x100+info[0x30+0x10*n+7]; - switch (model) { - case 0x43: - case 0x44: - size *= 0x200; - break; - case 0x42: - size *= 0x80; - break; - default: - GP_DEBUG("Unknown model, unknown size\n"); - return GP_ERROR_NOT_SUPPORTED; - } + GP_DEBUG("info[48+16*n+7] = %02X\n", info[48 + 16 * n + 7]); + size = info[0x30 + 0x10 * n + 6] * 0x100 +info[0x30 + 0x10 * n + 7]; + size *= priv->blocksize; GP_DEBUG("size = 0x%x = %d\n", size, size); return (size); } unsigned long -jl2005c_get_start_of_photo(CameraPrivateLibrary *priv, Info *info, +jl2005c_get_start_of_photo(CameraPrivateLibrary *priv, Info *info, unsigned int n) { unsigned long start; - unsigned char model = priv->model; - start = info[0x30+0x10*n+0x0c]*0x100+ - info[0x30+0x10*n+0x0d]; - start -= info[0x30+0x0c]*0x100+ - info[0x30+0x0d]; - switch (model) { - case 0x43: - case 0x44: - start *= 0x200; - break; - case 0x42: - start *= 0x80; - break; - default: - GP_DEBUG("Unknown model\n"); - return GP_ERROR_NOT_SUPPORTED; - } + start = info[0x30 + 0x10 * n + 0x0c] * 0x100 | + info[0x30 + 0x10 * n + 0x0d]; + start -= info[0x30 + 0x0c] * 0x100 | info[0x30 + 0x0d]; + start *= priv->blocksize; return start; } -int -set_usb_in_endpoint (Camera *camera, int inep) +int +set_usb_in_endpoint (Camera *camera, int inep) { GPPortSettings settings; gp_port_get_settings ( camera ->port, &settings); @@ -340,51 +259,46 @@ set_usb_in_endpoint (Camera *camera, int inep) settings.usb.inep = inep; GP_DEBUG("inep reset to %02X\n", inep); return gp_port_set_settings ( camera->port, settings); -} - - -int -jl2005c_get_picture_data (GPPort *port, - char *data, int size) +} +int +jl2005c_get_picture_data (GPPort *port, char *data, int size) { - /* inep has been reset to 0x82 already and does not get set back */ - /* We have to send 0b 00, presumably to access the data register, - * when starting to download the first photo only + /* inep has been reset to 0x82 already and does not get set back */ + /* We have to send 0b 00, presumably to access the data register, + * when starting to download the first photo only. But this is already + * done. So here we just download the data, between sleeps. */ usleep (10000); /*Data transfer begins*/ - gp_port_read (port, data, size); + gp_port_read (port, data, size); usleep (10000); return GP_OK; -} +} int jl2005c_reset (Camera *camera, GPPort *port) { - int blocksize = 0xfa00; + int downloadsize = 0xfa00; /* These cameras want all data to be dumped. If that is not yet done, * then do it now, before exiting! */ - while (camera->pl->bytes_read_from_camera < + while (camera->pl->bytes_read_from_camera < camera->pl->total_data_in_camera ) { if (! camera->pl->data_cache ) camera->pl->data_cache = malloc (0xfa00); - blocksize=0xfa00; + downloadsize=0xfa00; if (camera->pl->bytes_read_from_camera +0xfa00 >= - camera->pl->total_data_in_camera ) - blocksize = camera->pl->total_data_in_camera - + camera->pl->total_data_in_camera ) + downloadsize = camera->pl->total_data_in_camera - camera->pl->bytes_read_from_camera; - if(blocksize) + if(downloadsize) jl2005c_get_picture_data ( - camera->port, - (char *) camera->pl->data_cache, - blocksize); + camera->port, + (char *) camera->pl->data_cache, + downloadsize); camera->pl->bytes_read_from_camera - += blocksize; - + += downloadsize; } - - gp_port_write(port, "\x07\x00", 2); return GP_OK; } diff --git a/camlibs/jl2005c/jl2005c.h b/camlibs/jl2005c/jl2005c.h index 75bc4f499..fe945433d 100644 --- a/camlibs/jl2005c/jl2005c.h +++ b/camlibs/jl2005c/jl2005c.h @@ -1,16 +1,16 @@ /* jl2005c.h * - * Copyright (C) 2006 Theodore Kilgore <kilgota@auburn.edu> + * Copyright (C) 2006-2010 Theodore Kilgore <kilgota@auburn.edu> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. + * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the @@ -28,32 +28,28 @@ typedef unsigned char Info; struct _CameraPrivateLibrary { unsigned char model; -// unsigned char *catalog; + unsigned char init_done; + int can_do_capture; + int blocksize; int nb_entries; - int last_fetched_entry; unsigned long total_data_in_camera; unsigned long data_to_read; unsigned char *data_cache; unsigned long bytes_read_from_camera; - int data_used_from_block; unsigned long bytes_put_away; Info info[0xe000]; }; -int jl2005c_init (Camera *camera, GPPort *port, - CameraPrivateLibrary *priv); -int jl2005c_rewind (Camera *camera, GPPort *port); -int jl2005c_reset (Camera *camera, GPPort *port); -int jl2005c_get_num_pics (Info *info); -int jl2005c_get_resolution (Info *info, int n); +int jl2005c_init (Camera *camera, GPPort *port, CameraPrivateLibrary *priv); +int jl2005c_reset (Camera *camera, GPPort *port); +int jl2005c_get_num_pics (Info *info); -int jl2005c_get_compression (Info *info, int n); -int jl2005c_get_width (Info *info, int n); int jl2005c_get_pic_data_size (CameraPrivateLibrary *priv, Info *info, int n); -unsigned long jl2005c_get_start_of_photo(CameraPrivateLibrary *priv, +unsigned long jl2005c_get_start_of_photo (CameraPrivateLibrary *priv, Info *info, unsigned int n); -int set_usb_in_endpoint (Camera *camera, int inep); -int jl2005c_get_picture_data (GPPort *port, char *data, int size); +int set_usb_in_endpoint (Camera *camera, int inep); +int jl2005c_get_picture_data (GPPort *port, char *data, int size); + #endif diff --git a/camlibs/jl2005c/jpeg_memsrcdest.c b/camlibs/jl2005c/jpeg_memsrcdest.c new file mode 100644 index 000000000..efe650e9c --- /dev/null +++ b/camlibs/jl2005c/jpeg_memsrcdest.c @@ -0,0 +1,309 @@ +/* +* memsrc.c +* +* Copyright (C) 1994-1996, Thomas G. Lane. +* This file is part of the Independent JPEG Group's software. +* For conditions of distribution and use, see the accompanying README file. +* +* This file contains decompression data source routines for the case of +* reading JPEG data from a memory buffer that is preloaded with the entire +* JPEG file. This would not seem especially useful at first sight, but +* a number of people have asked for it. +* This is really just a stripped-down version of jdatasrc.c. Comparison +* of this code with jdatasrc.c may be helpful in seeing how to make +* custom source managers for other purposes. +*/ + +/* this is not a core library module, so it doesn't define JPEG_INTERNALS */ +#include <stdlib.h> +#include <stdio.h> +#include <jpeglib.h> +#include <jerror.h> +#include "jpeg_memsrcdest.h" + +/* libjpeg8 and later come with their own (API compatible) memory source + and dest */ +#if JPEG_LIB_VERSION < 80 + +/* Expanded data source object for memory input */ + +typedef struct { + struct jpeg_source_mgr pub; /* public fields */ + + JOCTET eoi_buffer[2]; /* a place to put a dummy EOI */ +} my_source_mgr; + +typedef my_source_mgr * my_src_ptr; + + +/* +* Initialize source --- called by jpeg_read_header +* before any data is actually read. +*/ + +METHODDEF(void) +init_source (j_decompress_ptr cinfo) +{ + /* No work, since jpeg_mem_src set up the buffer pointer and count. + * Indeed, if we want to read multiple JPEG images from one buffer, + * this *must* not do anything to the pointer. + */ +} + + +/* +* Fill the input buffer --- called whenever buffer is emptied. +* +* In this application, this routine should never be called; if it is called, +* the decompressor has overrun the end of the input buffer, implying we +* supplied an incomplete or corrupt JPEG datastream. A simple error exit +* might be the most appropriate response. +* +* But what we choose to do in this code is to supply dummy EOI markers +* in order to force the decompressor to finish processing and supply +* some sort of output image, no matter how corrupted. +*/ + +METHODDEF(boolean) +fill_input_buffer (j_decompress_ptr cinfo) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + + WARNMS(cinfo, JWRN_JPEG_EOF); + + /* Create a fake EOI marker */ + src->eoi_buffer[0] = (JOCTET) 0xFF; + src->eoi_buffer[1] = (JOCTET) JPEG_EOI; + src->pub.next_input_byte = src->eoi_buffer; + src->pub.bytes_in_buffer = 2; + + return TRUE; +} + + +/* +* Skip data --- used to skip over a potentially large amount of +* uninteresting data (such as an APPn marker). +* +* If we overrun the end of the buffer, we let fill_input_buffer deal with +* it. An extremely large skip could cause some time-wasting here, but +* it really isn't supposed to happen ... and the decompressor will never +* skip more than 64K anyway. +*/ + +METHODDEF(void) +skip_input_data (j_decompress_ptr cinfo, long num_bytes) +{ + my_src_ptr src = (my_src_ptr) cinfo->src; + + if (num_bytes > 0) { + while (num_bytes > (long) src->pub.bytes_in_buffer) { + num_bytes -= (long) src->pub.bytes_in_buffer; + (void) fill_input_buffer(cinfo); + /* note we assume that fill_input_buffer will never + * return FALSE, so suspension need not be handled. + */ + } + src->pub.next_input_byte += (size_t) num_bytes; + src->pub.bytes_in_buffer -= (size_t) num_bytes; + } +} + + +/* +* An additional method that can be provided by data source modules is the +* resync_to_restart method for error recovery in the presence of RST markers. +* For the moment, this source module just uses the default resync method +* provided by the JPEG library. That method assumes that no backtracking +* is possible. +*/ + + +/* +* Terminate source --- called by jpeg_finish_decompress +* after all data has been read. Often a no-op. +* +* NB: *not* called by jpeg_abort or jpeg_destroy; surrounding +* application must deal with any cleanup that should happen even +* for error exit. +*/ + +METHODDEF(void) +term_source (j_decompress_ptr cinfo) +{ + /* no work necessary here */ +} + + +/* +* Prepare for input from a memory buffer. +*/ + +GLOBAL(void) +jpeg_mem_src (j_decompress_ptr cinfo, unsigned char * buffer, + unsigned long bufsize) +{ + my_src_ptr src; + + /* The source object is made permanent so that a series of JPEG images + * can be read from a single buffer by calling jpeg_mem_src + * only before the first one. + * This makes it unsafe to use this manager and a different source + * manager serially with the same JPEG object. Caveat programmer. + */ + if (cinfo->src == NULL) { /* first time for this JPEG object? */ + cinfo->src = (struct jpeg_source_mgr *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, + JPOOL_PERMANENT, + sizeof(my_source_mgr)); + } + + src = (my_src_ptr) cinfo->src; + src->pub.init_source = init_source; + src->pub.fill_input_buffer = fill_input_buffer; + src->pub.skip_input_data = skip_input_data; + /* use default method */ + src->pub.resync_to_restart = jpeg_resync_to_restart; + src->pub.term_source = term_source; + + src->pub.next_input_byte = buffer; + src->pub.bytes_in_buffer = bufsize; +} + + + +/* Memory destination source modelled after Thomas G. Lane's memory source + * support and jdatadst.c + * + * Copyright (C) 2010, Hans de Goede + * + * This code may be used under the same conditions as Thomas G. Lane's memory + * source (see the copyright header at the top of this file). + */ + +typedef struct { + struct jpeg_destination_mgr pub; /* public fields */ + + JOCTET **buffer; /* start of buffer */ + unsigned long buf_size, *outsize; +} my_destination_mgr; + +typedef my_destination_mgr * my_dest_ptr; + +#define OUTPUT_BUF_SIZE 32768 /* choose an efficiently fwrite'able size */ + + +/* + * Initialize destination --- called by jpeg_start_compress + * before any data is actually written. + */ + +METHODDEF(void) +init_destination (j_compress_ptr cinfo) +{ + /* No work, since jpeg_mem_dest set up the buffer pointer and count. + * Indeed, if we want to write multiple JPEG images to one buffer, + * this *must* not do anything to the pointer. + */ +} + +/* + * Empty the output buffer --- called whenever buffer fills up. + * + * In typical applications, this should write the entire output buffer + * (ignoring the current state of next_output_byte & free_in_buffer), + * reset the pointer & count to the start of the buffer, and return TRUE + * indicating that the buffer has been dumped. + * + * In applications that need to be able to suspend compression due to output + * overrun, a FALSE return indicates that the buffer cannot be emptied now. + * In this situation, the compressor will return to its caller (possibly with + * an indication that it has not accepted all the supplied scanlines). The + * application should resume compression after it has made more room in the + * output buffer. Note that there are substantial restrictions on the use of + * suspension --- see the documentation. + * + * When suspending, the compressor will back up to a convenient restart point + * (typically the start of the current MCU). next_output_byte & free_in_buffer + * indicate where the restart point will be if the current call returns FALSE. + * Data beyond this point will be regenerated after resumption, so do not + * write it out when emptying the buffer externally. + */ + +METHODDEF(boolean) +empty_output_buffer (j_compress_ptr cinfo) +{ + my_dest_ptr dest = (my_dest_ptr) cinfo->dest; + + *dest->buffer = realloc (*dest->buffer, + dest->buf_size + OUTPUT_BUF_SIZE); + if (!*dest->buffer) + ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 0); + + dest->pub.next_output_byte = *dest->buffer + dest->buf_size; + dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; + dest->buf_size += OUTPUT_BUF_SIZE; + + return TRUE; +} + +/* + * Terminate destination --- called by jpeg_finish_compress + * after all data has been written. Usually needs to flush buffer. + * + * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding + * application must deal with any cleanup that should happen even + * for error exit. + */ + +METHODDEF(void) +term_destination (j_compress_ptr cinfo) +{ + my_dest_ptr dest = (my_dest_ptr) cinfo->dest; + + *dest->outsize = dest->buf_size - dest->pub.free_in_buffer; +} + +GLOBAL(void) +jpeg_mem_dest (j_compress_ptr cinfo, unsigned char ** outbuffer, + unsigned long * outsize) +{ + my_dest_ptr dest; + + /* The destination object is made permanent so that multiple JPEG + * images can be written to the same file without re-executing + * jpeg_stdio_dest. + * This makes it dangerous to use this manager and a different + * destination manager serially with the same JPEG object, because + * their private object sizes may be different. + * + * Caveat programmer. + */ + if (cinfo->dest == NULL) { /* first time for this JPEG object? */ + cinfo->dest = (struct jpeg_destination_mgr *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, + JPOOL_PERMANENT, + sizeof(my_destination_mgr)); + } + + dest = (my_dest_ptr) cinfo->dest; + dest->pub.init_destination = init_destination; + dest->pub.empty_output_buffer = empty_output_buffer; + dest->pub.term_destination = term_destination; + dest->buffer = outbuffer; + dest->buf_size = *outsize; + dest->outsize = outsize; + + if (*dest->buffer == NULL || dest->buf_size == 0) { + /* Allocate initial buffer */ + *dest->buffer = malloc(OUTPUT_BUF_SIZE); + if (*dest->buffer == NULL) + ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 10); + dest->buf_size = OUTPUT_BUF_SIZE; + } + + dest->pub.next_output_byte = *dest->buffer; + dest->pub.free_in_buffer = dest->buf_size; +} + +#endif diff --git a/camlibs/jl2005c/jpeg_memsrcdest.h b/camlibs/jl2005c/jpeg_memsrcdest.h new file mode 100644 index 000000000..e97118246 --- /dev/null +++ b/camlibs/jl2005c/jpeg_memsrcdest.h @@ -0,0 +1,9 @@ +#include <jpeglib.h> + +void +jpeg_mem_src (j_decompress_ptr cinfo, unsigned char * buffer, + unsigned long bufsize); + +void +jpeg_mem_dest (j_compress_ptr cinfo, unsigned char ** outbuffer, + unsigned long * outsize); diff --git a/camlibs/jl2005c/library.c b/camlibs/jl2005c/library.c index e94d2448f..783828326 100644 --- a/camlibs/jl2005c/library.c +++ b/camlibs/jl2005c/library.c @@ -24,9 +24,7 @@ #include <stdio.h> #include <string.h> -#include <bayer.h> -#include <gamma.h> - +#include "jl2005bcd_decompress.h" #include <gphoto2/gphoto2.h> @@ -60,6 +58,7 @@ struct { {"Sakar no. 75379", GP_DRIVER_STATUS_EXPERIMENTAL, 0x0979, 0x0227}, {"Sakar no. 81890", GP_DRIVER_STATUS_EXPERIMENTAL, 0x0979, 0x0227}, {"Sakar no. 91379", GP_DRIVER_STATUS_EXPERIMENTAL, 0x0979, 0x0227}, + {"Sakar no. 98379", GP_DRIVER_STATUS_EXPERIMENTAL, 0x0979, 0x0227}, {"Sakar Kidz-Cam no. 88379", GP_DRIVER_STATUS_EXPERIMENTAL, 0x0979, 0x0227}, {"Sakar clipshot no. 1169x", GP_DRIVER_STATUS_EXPERIMENTAL, @@ -79,7 +78,7 @@ struct { GP_DRIVER_STATUS_EXPERIMENTAL, 0x0979, 0x0227}, {"Vivitar Freelance", GP_DRIVER_STATUS_EXPERIMENTAL, 0x0979, 0x0227}, - {NULL,0,0,0,0} + {NULL,0,0,0} }; int @@ -121,14 +120,15 @@ camera_summary (Camera *camera, CameraText *summary, GPContext *context) int num_pics; num_pics = camera->pl->nb_entries; GP_DEBUG("camera->pl->nb_entries = %i\n",camera->pl->nb_entries); - sprintf (summary->text, + sprintf(summary->text, _("This camera contains a Jeilin JL2005%c chipset.\n" "The number of photos in it is %i. \n"), camera->pl->model, num_pics); return GP_OK; } -static int camera_manual (Camera *camera, CameraText *manual, GPContext *context) +static int +camera_manual (Camera *camera, CameraText *manual, GPContext *context) { strcpy(manual->text, _( @@ -182,11 +182,11 @@ get_file_func (CameraFilesystem *fs, const char *folder, const char *filename, { Camera *camera = user_data; int w, h = 0, b = 0, k; - unsigned char *pic_data, *pic_buffer; + unsigned char *pic_data, *pic_buffer, *pic_output = NULL; int HEADERSIZE=16; - unsigned char compressed; + int outputsize; unsigned long start_of_photo; - unsigned int blocksize = 0; + unsigned int downloadsize = 0; int filled = 0; GP_DEBUG ("Downloading pictures!\n"); @@ -195,29 +195,32 @@ get_file_func (CameraFilesystem *fs, const char *folder, const char *filename, /* Get the number of the photo on the camera */ k = gp_filesystem_number (camera->fs, "/", filename, context); - /* Determine the "compression" setting from the PAT table */ - compressed = (camera->pl->info[48+16*k+2]>>4) & 0x0f; - h = camera->pl->info[48+16*k+4]; - w = camera->pl->info[48+16*k+5]; + h = camera->pl->info[48 + 16 * k + 4] << 3; + w = camera->pl->info[48 + 16 * k + 5] << 3; GP_DEBUG ("height is %i\n", h); b = jl2005c_get_pic_data_size(camera->pl, camera->pl->info, k); - GP_DEBUG("b = %i = 0x%x bytes\n", b,b); + GP_DEBUG("b = %i = 0x%x bytes\n", b, b); start_of_photo = jl2005c_get_start_of_photo(camera->pl, camera->pl->info, k); GP_DEBUG("start_of_photo number %i = 0x%lx \n", k,start_of_photo); - pic_buffer = malloc (b+16); + pic_buffer = malloc(b + HEADERSIZE); if (!pic_buffer) return GP_ERROR_NO_MEMORY; - memset (pic_buffer, 0, b+16); - GP_DEBUG ("buffersize b+16 = %i = 0x%x bytes\n", b+16,b+16); - /* copy info line for photo from allocation table, as header */ - memcpy(pic_buffer, camera->pl->info+48+16*k, 16); - /* Camera can download in blocks of 0xfa00, with last block possibly - * smaller. So first we set up a cache of that size (if not done - * already) to hold raw data. + memset(pic_buffer, 0, b + HEADERSIZE); + GP_DEBUG("buffersize b + 16 = %i = 0x%x bytes\n", b + 16, b + 16); + /* Copy info line for photo from allocation table, as header */ + memcpy(pic_buffer, camera->pl->info + 48 + 16 * k, 16); + pic_data = pic_buffer + HEADERSIZE; + + /* + * Camera can download in blocks of 0xfa00, with only the last block + * possibly smaller. So first we set up a cache of that size + * (if it is not set up already) to hold raw data. If one tries + * instead to download one photo at a time, the camera will misbehave; + * data will be lost or corrupted. The dog will bite you, too. */ - pic_data = pic_buffer+HEADERSIZE; + if (!(camera->pl->data_cache)) { camera->pl->data_cache = malloc (0xfa00); } @@ -227,45 +230,51 @@ get_file_func (CameraFilesystem *fs, const char *folder, const char *filename, } /* Is there data in the cache, or not? If yes, read from it into the - * current photo, immediately. Update settings. But first a sanity - * check. + * current photo, immediately. Update settings. But first two sanity + * checks. */ if (start_of_photo < camera->pl->bytes_put_away) { GP_DEBUG("photo number %i starts in a funny place!\n",k); - /* Trouble. Start over again. */ + /* We need to start all over again to get this photo. */ jl2005c_reset(camera, camera->port); - jl2005c_rewind (camera, camera->port); - camera->pl->bytes_read_from_camera=0; + jl2005c_init (camera, camera->port, camera->pl); } if (start_of_photo + b > camera->pl->total_data_in_camera) { - GP_DEBUG("photo number %i ends in a funny place!\n",k); - GP_DEBUG("Blocksize may be wrong for this camera\n"); + GP_DEBUG("Photo runs past end of data. Exiting. \n"); + GP_DEBUG("Block size may be wrong for this camera\n"); return (GP_ERROR); } - - /* This while loop is entered if the photo number k-1 was not requested + /* + * This while loop is entered if the photo number k-1 was not requested * and thus has not been downloaded. The camera's rudimentary hardware * obliges us to download all data consecutively and toss whatever - * portion of said data that we do not intend to use. + * portion of said data that we do not intend to use. The rudimentary + * hardware also does not like to stop downloading at the end of one + * photo and then to start on the next. It wants to keep getting data + * in size 0xfa00 increments, and only the last block can be smaller. + * To do otherwise will cause data to be lost or corrupted. + * + * Whoever tries to simplify this convoluted and ugly procedure is + * warned that the obvious simplifications, while much prettier, + * just won't work. A kutya harap. */ while (camera->pl->bytes_read_from_camera <= start_of_photo) { - camera->pl->data_to_read = camera->pl->total_data_in_camera - camera->pl->bytes_read_from_camera; - blocksize = 0xfa00; - if (camera->pl->data_to_read < blocksize) - blocksize = camera->pl->data_to_read; - GP_DEBUG("blocksize = 0x%x\n", blocksize); - if(blocksize) + downloadsize = 0xfa00; + if (camera->pl->data_to_read < downloadsize) + downloadsize = camera->pl->data_to_read; + GP_DEBUG("downloadsize = 0x%x\n", downloadsize); + if (downloadsize) jl2005c_get_picture_data ( camera->port, (char *) camera->pl->data_cache, - blocksize); - camera->pl->bytes_read_from_camera += blocksize; + downloadsize); + camera->pl->bytes_read_from_camera += downloadsize; } - camera->pl->bytes_put_away=start_of_photo; + camera->pl->bytes_put_away = start_of_photo; if (camera->pl->bytes_read_from_camera > start_of_photo) { if(start_of_photo + b <= camera->pl->bytes_read_from_camera) { @@ -273,12 +282,14 @@ get_file_func (CameraFilesystem *fs, const char *folder, const char *filename, + (start_of_photo % 0xfa00) , b); camera->pl->bytes_put_away += b; - /* Photo data is contained in what is already + /* + * Photo data is contained in what is already * downloaded. * Jump immediately to process the photo. - */ + */ } else { - /* photo starts in one block and ends in another */ + /* Photo starts in one 0xfa00-sized download and ends + * in another */ filled = camera->pl->bytes_read_from_camera - start_of_photo; @@ -293,18 +304,19 @@ get_file_func (CameraFilesystem *fs, const char *folder, const char *filename, camera->pl->data_to_read = camera->pl->total_data_in_camera - camera->pl->bytes_read_from_camera; - blocksize = 0xfa00; - if (camera->pl->data_to_read < blocksize) - blocksize = camera->pl->data_to_read; - GP_DEBUG("blocksize = 0x%x\n", blocksize); - if(blocksize) + downloadsize = 0xfa00; + if (camera->pl->data_to_read < downloadsize) + downloadsize = camera->pl->data_to_read; + GP_DEBUG("downloadsize = 0x%x\n", downloadsize); + if (downloadsize) jl2005c_get_picture_data ( camera->port, (char *) camera->pl->data_cache, - blocksize); - camera->pl->bytes_read_from_camera += blocksize; + downloadsize); + camera->pl->bytes_read_from_camera += downloadsize; - if (camera->pl->bytes_read_from_camera >= start_of_photo + b ) { + if (camera->pl->bytes_read_from_camera >= + start_of_photo + b ) { GP_DEBUG("THIS ONE?\n"); memcpy(pic_data + filled, camera->pl->data_cache, b - filled); @@ -312,20 +324,45 @@ get_file_func (CameraFilesystem *fs, const char *folder, const char *filename, break; } else { GP_DEBUG("THIS ONE??\n"); - if (!blocksize) + if (!downloadsize) break; - memcpy(pic_data + filled, - camera->pl->data_cache, blocksize); - camera->pl->bytes_put_away += blocksize; - filled += blocksize; + memcpy(pic_data + filled, + camera->pl->data_cache, downloadsize); + camera->pl->bytes_put_away += downloadsize; + filled += downloadsize; } } - if (GP_FILE_TYPE_RAW == type) { + if (type == GP_FILE_TYPE_RAW) { gp_file_set_mime_type(file, GP_MIME_RAW); - gp_file_set_data_and_size(file, (char *)pic_buffer , b + 16); + gp_file_set_data_and_size(file, (char *)pic_buffer, b + 16); return GP_OK; - } else return GP_ERROR_NOT_SUPPORTED; + } else if (type == GP_FILE_TYPE_PREVIEW) { + if (!camera->pl->can_do_capture) + return GP_ERROR_NOT_SUPPORTED; + outputsize = (pic_buffer[9] & 0xf0) * 192 + 256; + if (outputsize == 256) + return GP_ERROR_NOT_SUPPORTED; + pic_output = calloc(outputsize, 1); + if (!pic_output) + return GP_ERROR_NO_MEMORY; + outputsize = jl2005bcd_decompress(pic_output, pic_buffer, + b + 16, 1); + gp_file_set_mime_type(file, GP_MIME_PPM); + gp_file_set_data_and_size(file, (char *)pic_output, + outputsize); + } else if (type == GP_FILE_TYPE_NORMAL) { + outputsize = 3 * w * h + 256; + pic_output = calloc(outputsize, 1); + if (!pic_output) + return GP_ERROR_NO_MEMORY; + outputsize = jl2005bcd_decompress(pic_output, pic_buffer, + b + 16, 0); + gp_file_set_mime_type(file, GP_MIME_PPM); + gp_file_set_data_and_size(file, (char *)pic_output, + outputsize); + } else + return GP_ERROR_NOT_SUPPORTED; return GP_OK; } @@ -353,7 +390,7 @@ static CameraFilesystemFuncs fsfuncs = { }; int -camera_init(Camera *camera, GPContext *context) +camera_init (Camera *camera, GPContext *context) { GPPortSettings settings; int ret = 0; @@ -363,7 +400,7 @@ camera_init(Camera *camera, GPContext *context) camera->functions->summary = camera_summary; camera->functions->about = camera_about; camera->functions->exit = camera_exit; - + GP_DEBUG ("Initializing the camera\n"); ret = gp_port_get_settings(camera->port,&settings); if (ret < 0) return ret; @@ -397,13 +434,12 @@ camera_init(Camera *camera, GPContext *context) if (!camera->pl) return GP_ERROR_NO_MEMORY; memset (camera->pl, 0, sizeof (CameraPrivateLibrary)); /* Connect to the camera */ - camera->pl->bytes_read_from_camera = 0; camera->pl->total_data_in_camera=0; camera->pl->data_to_read = 0; - camera->pl->data_used_from_block = 0; camera->pl->bytes_put_away = 0; camera->pl->data_cache = NULL; - jl2005c_init (camera,camera->port, camera->pl); + camera->pl->init_done = 0; + jl2005c_init (camera, camera->port, camera->pl); return GP_OK; } |