This is basically a continuation of the work from #1002 but her for the camlibs directory. The purpose was to replace spaces as a means for indentation with tab characters. Note: there are some files which are still offending the general rule but are (mostly) consistent within themselves. I let them be for the moment. Fixing them would basically replace the whole file. Also: there are still a ton of whitespace related inconsistencies like "x=10" vs "x = 10" or "func(10)" vs "func (10)". This is a whitespace only change (if you include newlines as whitespace).
477 lines
11 KiB
C
477 lines
11 KiB
C
/*
|
|
* digita.c
|
|
*
|
|
* Copyright 1999-2001 Johannes Erdfelt
|
|
*
|
|
* 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., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#define _DEFAULT_SOURCE
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#ifdef HAVE_LIMITS_H
|
|
#include <limits.h>
|
|
#endif
|
|
|
|
#define GP_MODULE "digita"
|
|
|
|
#include <gphoto2/gphoto2.h>
|
|
#include <gphoto2/gphoto2-port.h>
|
|
|
|
#include "libgphoto2/i18n.h"
|
|
|
|
|
|
#include "libgphoto2/gphoto2-endian.h"
|
|
#include "digita.h"
|
|
|
|
#ifndef MAX
|
|
# define MAX(a, b) ((a) > (b) ? (a) : (b))
|
|
#endif
|
|
#ifndef MIN
|
|
# define MIN(a, b) ((a) < (b) ? (a) : (b))
|
|
#endif
|
|
|
|
static const struct {
|
|
char *model;
|
|
int usb_vendor;
|
|
int usb_product;
|
|
} models[] = {
|
|
{"Kodak:DC220", 0x040A, 0x0100 },
|
|
{"Kodak:DC260", 0x040A, 0x0110 },
|
|
{"Kodak:DC265", 0x040A, 0x0111 },
|
|
{"Kodak:DC290", 0x040A, 0x0112 },
|
|
{"HP:PhotoSmart C500", 0x03F0, 0x4102 },
|
|
{"HP:PhotoSmart 618", 0x03F0, 0x4102 },
|
|
{"HP:PhotoSmart 912", 0x03F0, 0x4102 },
|
|
{"HP:PhotoSmart C500 2",0xf003, 0x6002 },
|
|
};
|
|
|
|
int camera_abilities(CameraAbilitiesList *list)
|
|
{
|
|
unsigned int i;
|
|
CameraAbilities a;
|
|
|
|
for (i = 0; i < sizeof(models) / sizeof(models[0]); i++) {
|
|
memset(&a, 0, sizeof(a));
|
|
strcpy(a.model, models[i].model);
|
|
a.status = GP_DRIVER_STATUS_PRODUCTION;
|
|
a.port = GP_PORT_SERIAL | GP_PORT_USB;
|
|
a.speed[0] = 9600;
|
|
a.speed[1] = 19200;
|
|
a.speed[2] = 38400;
|
|
a.speed[3] = 57600;
|
|
a.speed[4] = 115200;
|
|
a.speed[5] = 0;
|
|
a.operations = GP_OPERATION_NONE;
|
|
a.folder_operations = GP_FOLDER_OPERATION_NONE;
|
|
a.file_operations = GP_FILE_OPERATION_PREVIEW |
|
|
GP_FILE_OPERATION_DELETE;
|
|
a.usb_vendor = models[i].usb_vendor;
|
|
a.usb_product = models[i].usb_product;
|
|
|
|
gp_abilities_list_append(list, a);
|
|
}
|
|
|
|
return GP_OK;
|
|
}
|
|
|
|
int camera_id(CameraText *id)
|
|
{
|
|
strcpy(id->text, "digita");
|
|
|
|
return GP_OK;
|
|
}
|
|
|
|
static int camera_exit(Camera *camera, GPContext *context)
|
|
{
|
|
if (camera->pl) {
|
|
free(camera->pl);
|
|
camera->pl = NULL;
|
|
}
|
|
|
|
return GP_OK;
|
|
}
|
|
|
|
static int folder_list_func(CameraFilesystem *fs, const char *folder,
|
|
CameraList *list, void *data, GPContext *context)
|
|
{
|
|
Camera *camera = data;
|
|
int i, i1;
|
|
|
|
if (digita_get_file_list(camera->pl) < 0)
|
|
return GP_ERROR;
|
|
|
|
if (folder[0] == '/')
|
|
folder++;
|
|
|
|
/* Walk through all of the pictures building a list of folders */
|
|
for (i = 0; i < camera->pl->num_pictures; i++) {
|
|
int found;
|
|
char *path,*newfolder;
|
|
|
|
/* Check to make sure the path matches the folder we're */
|
|
/* looking through */
|
|
if (strlen(folder) && strncmp(camera->pl->file_list[i].fn.path,
|
|
folder, strlen(folder)))
|
|
continue;
|
|
|
|
/* If it's not the root, then we skip the / as well */
|
|
if (strlen(folder))
|
|
path = camera->pl->file_list[i].fn.path + strlen(folder) + 1;
|
|
else
|
|
path = camera->pl->file_list[i].fn.path;
|
|
|
|
if (!strlen(path))
|
|
continue;
|
|
|
|
if (strchr(path, '/') != path + strlen(path) - 1)
|
|
continue;
|
|
|
|
newfolder = strdup(path);
|
|
*strchr(newfolder,'/') = 0;
|
|
found = 0;
|
|
for (i1 = 0; i1 < gp_list_count(list); i1++) {
|
|
const char *name;
|
|
|
|
gp_list_get_name(list, i1, &name);
|
|
if (!strcmp(name, newfolder)) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found)
|
|
gp_list_append(list, newfolder, NULL);
|
|
free (newfolder);
|
|
}
|
|
|
|
return GP_OK;
|
|
}
|
|
|
|
static int file_list_func(CameraFilesystem *fs, const char *folder,
|
|
CameraList *list, void *data, GPContext *context)
|
|
{
|
|
Camera *camera = data;
|
|
int i;
|
|
|
|
/* We should probably check to see if we have a list already and */
|
|
/* get the changes since */
|
|
if (digita_get_file_list(camera->pl) < 0)
|
|
return GP_ERROR;
|
|
|
|
if (folder[0] == '/')
|
|
folder++;
|
|
|
|
/* Walk through all of the pictures building a list of folders */
|
|
for (i = 0; i < camera->pl->num_pictures; i++) {
|
|
if (strncmp(camera->pl->file_list[i].fn.path, folder, strlen(folder)) ||
|
|
camera->pl->file_list[i].fn.path[strlen(folder)] != '/')
|
|
continue;
|
|
|
|
gp_list_append(list, camera->pl->file_list[i].fn.dosname, NULL);
|
|
}
|
|
|
|
return GP_OK;
|
|
}
|
|
|
|
#define GFD_BUFSIZE 19432
|
|
static unsigned char *digita_file_get(Camera *camera, const char *folder,
|
|
const char *filename, int thumbnail, int *size,
|
|
GPContext *context)
|
|
{
|
|
struct filename fn;
|
|
struct partial_tag tag;
|
|
unsigned char *data;
|
|
int pos, len, buflen;
|
|
unsigned int id;
|
|
|
|
GP_DEBUG ("Getting %s from folder %s...", filename, folder);
|
|
|
|
/* Setup the filename */
|
|
/* FIXME: This is kinda lame, but it's a quick hack */
|
|
fn.driveno = camera->pl->file_list[0].fn.driveno;
|
|
strcpy(fn.path, folder);
|
|
strcat(fn.path, "/");
|
|
strcpy(fn.dosname, filename);
|
|
|
|
/* How much data we're willing to accept */
|
|
tag.offset = htobe32(0);
|
|
tag.length = htobe32(GFD_BUFSIZE);
|
|
tag.filesize = htobe32(0);
|
|
|
|
buflen = GFD_BUFSIZE;
|
|
data = malloc(buflen);
|
|
if (!data) {
|
|
GP_DEBUG( "digita_file_get: allocating memory");
|
|
return NULL;
|
|
}
|
|
memset(data, 0, buflen);
|
|
|
|
if (digita_get_file_data(camera->pl, thumbnail, &fn, &tag, data) < 0) {
|
|
GP_DEBUG( "digita_get_picture: digita_get_file_data failed");
|
|
free (data);
|
|
return NULL;
|
|
}
|
|
|
|
buflen = be32toh(tag.filesize);
|
|
if (thumbnail)
|
|
buflen += 16;
|
|
|
|
data = realloc(data, buflen);
|
|
if (!data) {
|
|
GP_DEBUG( "digita_file_get: couldn't reallocate memory");
|
|
return NULL;
|
|
}
|
|
|
|
len = be32toh(tag.filesize);
|
|
pos = be32toh(tag.length);
|
|
id = gp_context_progress_start (context, len, _("Getting file..."));
|
|
while (pos < len) {
|
|
gp_context_progress_update (context, id, pos);
|
|
|
|
tag.offset = htobe32(pos);
|
|
if ((len - pos) > GFD_BUFSIZE)
|
|
tag.length = htobe32(GFD_BUFSIZE);
|
|
else
|
|
tag.length = htobe32(len - pos);
|
|
|
|
if (digita_get_file_data(camera->pl, thumbnail, &fn, &tag, data + pos) < 0) {
|
|
GP_DEBUG ("digita_get_file_data failed.");
|
|
free (data);
|
|
return NULL;
|
|
}
|
|
pos += be32toh(tag.length);
|
|
}
|
|
gp_context_progress_stop (context, id);
|
|
|
|
*size = buflen;
|
|
|
|
return data;
|
|
}
|
|
|
|
/* Colorspace conversion is voodoo-magic to me --jerdfelt */
|
|
static void decode_ycc422(unsigned char *input, int width, int height, unsigned char *output)
|
|
{
|
|
int x, y;
|
|
|
|
for (y = 0; y < height; y++) {
|
|
for (x = 0; x < width / 2; x++) {
|
|
int _y, u, y1, v, r, g, b;
|
|
|
|
#define LIMIT(_x) ((((_x)>0xffffff)?0xff0000:(((_x)<=0xffff)?0:(_x)&0xff0000))>>16)
|
|
u = *input++ - 128;
|
|
_y = *input++ - 16;
|
|
v = *input++ - 128;
|
|
y1 = *input++ - 16;
|
|
r = 104635 * v;
|
|
g = -25690 * u + -53294 * v;
|
|
b = 132278 * u;
|
|
_y *= 76310;
|
|
y1 *= 76310;
|
|
*output++ = LIMIT(r + _y);
|
|
*output++ = LIMIT(g + _y);
|
|
*output++ = LIMIT(b + _y);
|
|
*output++ = LIMIT(r + y1);
|
|
*output++ = LIMIT(g + y1);
|
|
*output++ = LIMIT(b + y1);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int get_file_func(CameraFilesystem *fs, const char *folder,
|
|
const char *filename, CameraFileType type,
|
|
CameraFile *file, void *user_data, GPContext *context)
|
|
{
|
|
Camera *camera = user_data;
|
|
unsigned char *data;
|
|
unsigned int width, height;
|
|
char ppmhead[64];
|
|
int buflen;
|
|
|
|
if (folder[0] == '/')
|
|
folder++;
|
|
|
|
switch (type) {
|
|
case GP_FILE_TYPE_NORMAL:
|
|
GP_DEBUG("digita/get_file_func: Getting picture");
|
|
data = digita_file_get(camera, folder, filename, 0, &buflen, context);
|
|
break;
|
|
case GP_FILE_TYPE_PREVIEW:
|
|
GP_DEBUG("digita/get_file_func: Getting thumbnail");
|
|
data = digita_file_get(camera, folder, filename, 1, &buflen, context);
|
|
break;
|
|
default:
|
|
gp_context_error(context, _("Image type is not supported"));
|
|
return GP_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (!data)
|
|
return GP_ERROR;
|
|
|
|
switch (type) {
|
|
case GP_FILE_TYPE_NORMAL:
|
|
gp_file_set_data_and_size(file, (char*)data, buflen);
|
|
gp_file_set_mime_type(file, GP_MIME_JPEG);
|
|
break;
|
|
case GP_FILE_TYPE_PREVIEW:
|
|
{
|
|
char *ppm;
|
|
|
|
memcpy((void *)&height, data + 4, 4);
|
|
height = be32toh(height);
|
|
memcpy((void *)&width, data + 8, 4);
|
|
width = be32toh(width);
|
|
|
|
GP_DEBUG( "picture size %dx%d", width, height);
|
|
GP_DEBUG( "data size %d", buflen - 16);
|
|
|
|
sprintf(ppmhead,
|
|
"P6\n"
|
|
"# CREATOR: gphoto2, digita library\n"
|
|
"%i %i\n"
|
|
"255\n", width, height);
|
|
ppm = malloc(strlen(ppmhead) + (width * height * 3));
|
|
if (!ppm)
|
|
return -1;
|
|
|
|
strcpy(ppm, ppmhead);
|
|
|
|
decode_ycc422(data + 16, width, height, (unsigned char*)(ppm + strlen(ppmhead)));
|
|
free(data);
|
|
|
|
gp_file_set_mime_type(file, GP_MIME_PPM);
|
|
gp_file_set_data_and_size(file, ppm, strlen(ppmhead) + (width * height * 3));
|
|
break;
|
|
}
|
|
default:
|
|
return GP_ERROR_NOT_SUPPORTED;
|
|
}
|
|
|
|
return GP_OK;
|
|
}
|
|
|
|
static int camera_summary(Camera *camera, CameraText *summary, GPContext *context)
|
|
{
|
|
int taken;
|
|
|
|
if (digita_get_storage_status(camera->pl, &taken, NULL, NULL) < 0)
|
|
return 0;
|
|
|
|
sprintf(summary->text, _("Number of pictures: %d"), taken);
|
|
|
|
return GP_OK;
|
|
}
|
|
|
|
static int camera_about(Camera *camera, CameraText *about, GPContext *context)
|
|
{
|
|
strcpy(about->text, _("Digita\n" \
|
|
"Johannes Erdfelt <johannes@erdfelt.com>"));
|
|
|
|
return GP_OK;
|
|
}
|
|
|
|
static int digita_file_delete(Camera *camera, const char *folder,
|
|
const char *filename, GPContext *context)
|
|
{
|
|
struct filename fn;
|
|
|
|
/* Setup the filename */
|
|
/* FIXME: This is kinda lame, but it's a quick hack */
|
|
fn.driveno = camera->pl->file_list[0].fn.driveno;
|
|
strcpy(fn.path, folder);
|
|
strcat(fn.path, "/");
|
|
strcpy(fn.dosname, filename);
|
|
|
|
if (digita_delete_picture(camera->pl, &fn) < 0)
|
|
return 0;
|
|
|
|
if (digita_get_file_list(camera->pl) < 0)
|
|
return 0;
|
|
|
|
return GP_OK;
|
|
}
|
|
|
|
static int delete_file_func(CameraFilesystem *fs, const char *folder,
|
|
const char *filename, void *user_data, GPContext *context)
|
|
{
|
|
Camera *camera = user_data;
|
|
|
|
if (folder[0] == '/')
|
|
folder++;
|
|
|
|
return digita_file_delete(camera, folder, filename, context);
|
|
}
|
|
|
|
static CameraFilesystemFuncs fsfuncs = {
|
|
.file_list_func = file_list_func,
|
|
.folder_list_func = folder_list_func,
|
|
.get_file_func = get_file_func,
|
|
.del_file_func = delete_file_func
|
|
};
|
|
|
|
int camera_init(Camera *camera, GPContext *context)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (!camera)
|
|
return GP_ERROR;
|
|
|
|
/* First, set up all the function pointers */
|
|
camera->functions->exit = camera_exit;
|
|
camera->functions->summary = camera_summary;
|
|
camera->functions->about = camera_about;
|
|
|
|
/* Set up the CameraFilesystem */
|
|
gp_filesystem_set_funcs(camera->fs, &fsfuncs, camera);
|
|
|
|
GP_DEBUG( "Initializing the camera");
|
|
|
|
camera->pl = malloc(sizeof(CameraPrivateLibrary));
|
|
if (!camera->pl)
|
|
return GP_ERROR_NO_MEMORY;
|
|
memset(camera->pl, 0, sizeof(CameraPrivateLibrary));
|
|
camera->pl->gpdev = camera->port;
|
|
|
|
switch (camera->port->type) {
|
|
case GP_PORT_USB:
|
|
ret = digita_usb_open(camera->pl, camera);
|
|
break;
|
|
case GP_PORT_SERIAL:
|
|
ret = digita_serial_open(camera->pl, camera);
|
|
break;
|
|
default:
|
|
free(camera->pl);
|
|
camera->pl = NULL;
|
|
return GP_ERROR_UNKNOWN_PORT;
|
|
}
|
|
|
|
if (ret < 0) {
|
|
GP_DEBUG( "camera_init: couldn't open digita device");
|
|
free(camera->pl);
|
|
camera->pl = NULL;
|
|
return ret;
|
|
}
|
|
|
|
return GP_OK;
|
|
}
|
|
|