Back to index

lightning-sunbird  0.9+nobinonly
Classes | Defines | Typedefs | Functions | Variables
nsSanePlugin.cpp File Reference
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <setjmp.h>
#include <gdk/gdk.h>
#include <gdk/gdkprivate.h>
#include <gtk/gtk.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "nsIIOService.h"
#include "nsSanePlugin.h"
#include "plstr.h"
#include "prmem.h"
#include "nsIEventQueueService.h"
#include "nsIEventQueue.h"
#include "jpeglib.h"
#include "sane/sane.h"
#include "sane/sanei.h"
#include "sane/saneopts.h"

Go to the source code of this file.

Classes

struct  my_JPEG_error_mgr

Defines

#define MAX_OPT_SIZE   (1024 * 2)
#define MAX_LINE_ATTRIBUTE_LENGTH   20
#define MAX_PARAM_LEN   256
#define MAX_DEVICE_SIZE   256

Typedefs

typedef struct my_JPEG_error_mgremptr

Functions

static void PR_CALLBACK ThreadedDestroyEvent (PLEvent *aEvent)
static void PR_CALLBACK ThreadedHandleEvent (PLEvent *aEvent)
static void store_filename (GtkFileSelection *selector, gpointer user_data)
static nsresult optioncat (char **dest, const char *src, const char *ending)
static void my_jpeg_error_exit (j_common_ptr cinfo)
static unsigned char * jpeg_file_to_rgb (FILE *f, int *w, int *h)
static gboolean draw (GtkWidget *widget, GdkEventExpose *event, gpointer data)
static unsigned char * crop_image (unsigned char *rgb_data, int *rgb_width, int *rgb_height, gint x, gint y, gint w, gint h)
static unsigned char * scale_image (unsigned char *rgb_data, int rgb_width, int rgb_height, int w, int h)
static NS_DEFINE_CID (kCPluginManagerCID, NS_PLUGINMANAGER_CID)
static NS_DEFINE_CID (kEventQueueService, NS_EVENTQUEUESERVICE_CID)
 NS_IMPL_ISUPPORTS2 (nsSanePluginInstance, nsIPluginInstance, nsISanePluginInstance) NS_METHOD nsSanePluginInstance
void PR_CALLBACK scanimage_thread_routine (void *arg)

Variables

static PRInt32 gPluginObjectCount = 0

Class Documentation

struct my_JPEG_error_mgr

Definition at line 71 of file nsSanePlugin.cpp.

Class Members
sigjmp_buf setjmp_buffer

Define Documentation

Definition at line 1024 of file nsSanePlugin.cpp.

#define MAX_OPT_SIZE   (1024 * 2)

Definition at line 69 of file nsSanePlugin.cpp.

#define MAX_PARAM_LEN   256

Typedef Documentation

typedef struct my_JPEG_error_mgr* emptr

Definition at line 76 of file nsSanePlugin.cpp.


Function Documentation

unsigned char * crop_image ( unsigned char *  rgb_data,
int rgb_width,
int rgb_height,
gint  x,
gint  y,
gint  w,
gint  h 
) [static]

Definition at line 2336 of file nsSanePlugin.cpp.

{
  unsigned char      *data;
  int                 xx, yy, w3, w4;
  unsigned char      *ptr1, *ptr2;

  if (!rgb_data)
    return NULL;

  if (x < 0)
    {
      w += x;
      x = 0;
    }
  if (y < 0)
    {
      h += y;
      y = 0;
    }
  if (x >= *rgb_width)
    return NULL;
  if (y >= *rgb_height)
    return NULL;
  if (w <= 0)
    return NULL;
  if (h <= 0)
    return NULL;
  if (x + w > *rgb_width)
    w = *rgb_width - x;
  if (y + h > *rgb_height)
    h = *rgb_height - y;
  if (w <= 0)
    return NULL;
  if (h <= 0)
    return NULL;

  w3 = *rgb_width * 3;
  w4 = (*rgb_width - w) * 3;
  data = (unsigned char *)PR_Malloc(w * h * 3);
  if (data == NULL)
    return NULL;
  ptr1 = rgb_data + (y * w3) + (x * 3);
  ptr2 = data;
  for (yy = 0; yy < h; yy++)
    {
      for (xx = 0; xx < w; xx++)
       {
         *ptr2++ = *ptr1++;
         *ptr2++ = *ptr1++;
         *ptr2++ = *ptr1++;
       }
      ptr1 += w4;
    }
  
  *rgb_width = w;
  *rgb_height = h;

  return data;
}
gboolean draw ( GtkWidget *  widget,
GdkEventExpose *  event,
gpointer  data 
) [static]

Definition at line 2105 of file nsSanePlugin.cpp.

                                                                        {

    nsSanePluginInstance * pthis = (nsSanePluginInstance *)data;

    pthis->PaintImage();
    return TRUE;
}

Here is the call graph for this function:

Here is the caller graph for this function:

unsigned char * jpeg_file_to_rgb ( FILE f,
int w,
int h 
) [static]

Definition at line 2160 of file nsSanePlugin.cpp.

{
  struct jpeg_decompress_struct cinfo;
  struct my_JPEG_error_mgr jerr;
  unsigned char      *data, *line[16], *ptr;
  int                 x, y, i;

  cinfo.err = jpeg_std_error(&(jerr.pub));
  jerr.pub.error_exit = my_jpeg_error_exit;

  /* error handler to longjmp to, we want to preserve signals */
  if (sigsetjmp(jerr.setjmp_buffer, 1)) {
    
    /* Whoops there was a jpeg error */
    jpeg_destroy_decompress(&cinfo);
    return NULL;
  }

  jpeg_create_decompress(&cinfo);
  jpeg_stdio_src(&cinfo, f);
  jpeg_read_header(&cinfo, TRUE);
  cinfo.do_fancy_upsampling = FALSE;
  cinfo.do_block_smoothing = FALSE;

  cinfo.scale_num = 2;
  cinfo.scale_denom = 1;
  jpeg_start_decompress(&cinfo);
  *w = cinfo.output_width;
  *h = cinfo.output_height;
  data = (unsigned char *)PR_Malloc(*w ** h * 3);
  if (!data) {
    NS_ERROR("jpeg_file_to_rgb: Error trying to allocate buffer!\n");
    jpeg_destroy_decompress(&cinfo);
    return NULL;
  }
  ptr = data;

  if (cinfo.rec_outbuf_height > 16) {
      NS_ERROR("ERROR: JPEG uses line buffers > 16. Cannot load.\n");
      return NULL;
  }

  if (cinfo.output_components == 3) {
    for (y = 0; y < *h; y += cinfo.rec_outbuf_height) {
      for (i = 0; i < cinfo.rec_outbuf_height; i++) {
       line[i] = ptr;
       ptr += *w * 3;
      }

      jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height);
    }
  } else if (cinfo.output_components == 1) {
    for (i = 0; i < cinfo.rec_outbuf_height; i++) {
      if ((line[i] = (unsigned char *)PR_Malloc(*w)) == NULL) {
       int                 t = 0;
       
       NS_ERROR("jpeg_file_to_rgb: Error tying to allocate line!\n");
       
       for (t = 0; t < i; t++)
         PR_FREEIF(line[t]);
       jpeg_destroy_decompress(&cinfo);
       return NULL;
      }
    }

    for (y = 0; y < *h; y += cinfo.rec_outbuf_height) {
      jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height);
      for (i = 0; i < cinfo.rec_outbuf_height; i++) {
       for (x = 0; x < *w; x++) {
         *ptr++ = line[i][x];
         *ptr++ = line[i][x];
         *ptr++ = line[i][x];
       }
      }
    }

    for (i = 0; i < cinfo.rec_outbuf_height; i++)
      PR_FREEIF(line[i]);
  }

  jpeg_finish_decompress(&cinfo);
  jpeg_destroy_decompress(&cinfo);

  return data;
}

Here is the call graph for this function:

Here is the caller graph for this function:

void my_jpeg_error_exit ( j_common_ptr  cinfo) [static]

Definition at line 2149 of file nsSanePlugin.cpp.

{
    emptr errmgr;

    errmgr = (emptr) cinfo->err;
    cinfo->err->output_message(cinfo);
    siglongjmp(errmgr->setjmp_buffer, 1);
    return;
}

Here is the caller graph for this function:

static NS_DEFINE_CID ( kCPluginManagerCID  ,
NS_PLUGINMANAGER_CID   
) [static]
static NS_DEFINE_CID ( kEventQueueService  ,
NS_EVENTQUEUESERVICE_CID   
) [static]

Definition at line 179 of file nsSanePlugin.cpp.

{
#ifdef DEBUG
    printf("nsSanePluginInstance::Initialize()\n");
#endif

    NS_ASSERTION(peer != NULL, "null peer");

    nsIPluginTagInfo * taginfo;
    const char * const * names = nsnull;
    const char * const * values = nsnull;
    PRUint16 count = 0;
    nsresult result;

    gdk_rgb_init();

    fPeer = peer;
    peer->AddRef();
    result = peer->GetMode( &fMode );

    if ( NS_FAILED( result ) )
        return result;

    result = peer->QueryInterface( nsIPluginTagInfo::GetIID(),
                                   ( void ** )&taginfo );

    if ( NS_SUCCEEDED( result ) )
    {
        taginfo->GetAttributes( count, names, values );
        NS_IF_RELEASE( taginfo );
    }

    // Initialize default line attribute info
    mLineWidth = 5;
    mLineStyle = GDK_LINE_ON_OFF_DASH;
    mCapStyle = GDK_CAP_BUTT;
    mJoinStyle = GDK_JOIN_MITER;

    // Get user requested attribute info
    for (int i=0; i < count; i++) {

        if (PL_strcasecmp(names[i], "line_width") == 0) {
            // Line width
            sscanf(values[i], "%i", &mLineWidth);

        } else if (PL_strcasecmp(names[i], "line_style") == 0) {
            // Line style
            if (PL_strcasecmp(values[i], "dash") == 0) 
                mLineStyle = GDK_LINE_ON_OFF_DASH;
            else if (PL_strcasecmp(values[i], "solid") == 0)
                mLineStyle = GDK_LINE_SOLID;
            else if (PL_strcasecmp(values[i], "double_dash") == 0)
                mLineStyle = GDK_LINE_DOUBLE_DASH;

        } else if (PL_strcasecmp(names[i], "cap_style") == 0) {
            // Cap style
            if (PL_strcasecmp(values[i], "round") == 0) 
                mCapStyle = GDK_CAP_ROUND;
            else if (PL_strcasecmp(values[i], "projecting") == 0)
                mCapStyle = GDK_CAP_PROJECTING;
            else if (PL_strcasecmp(values[i], "butt") == 0)
                mCapStyle = GDK_CAP_BUTT;
            else if (PL_strcasecmp(values[i], "not_last") == 0)
                mCapStyle = GDK_CAP_NOT_LAST;

        } else if (PL_strcasecmp(names[i], "join_style") == 0) {
            // Join style
            if (PL_strcasecmp(values[i], "miter") == 0) 
                mJoinStyle = GDK_JOIN_MITER;
            else if (PL_strcasecmp(values[i], "round") == 0)
                mJoinStyle = GDK_JOIN_ROUND;
            else if (PL_strcasecmp(values[i], "bevel") == 0)
                mJoinStyle = GDK_JOIN_BEVEL;

        } else if (PL_strcasecmp(names[i], "device") == 0) {
            // Device
            PR_FREEIF(mSaneDevice);
            mSaneDevice = PL_strdup(values[i]);
        } else if (PL_strcasecmp(names[i], "onScanComplete") == 0) {
            // onScanComplete Callback
            PR_FREEIF(mOnScanCompleteScript);
            mOnScanCompleteScript = PL_strdup(values[i]);
        } else if(PL_strcasecmp(names[i], "onInitComplete") == 0) {
            // onInitComplete Callback
            PR_FREEIF(mOnInitCompleteScript);
            mOnInitCompleteScript = PL_strdup(values[i]);
        }
       }
    
    // the mImageFilename is used to store data directly 
    // from the sane device and also rendering to the screen
    sprintf( mImageFilename, "/tmp/nsSanePlugin.%p.jpg", (void *)this );

    PlatformNew();   /* Call Platform-specific initializations */
    return NS_OK;

}

Here is the call graph for this function:

nsresult optioncat ( char **  dest,
const char *  src,
const char *  ending 
) [static]

Definition at line 2113 of file nsSanePlugin.cpp.

{
    char * p;
    int i;

    NS_ASSERTION(dest != NULL, "null option string pointer");
    if (!dest)
        return NS_ERROR_NULL_POINTER;

    NS_ASSERTION(src != NULL, "null option string pointer");
    if (!src)
        return NS_ERROR_NULL_POINTER;

    NS_ASSERTION(ending != NULL, "null option string pointer");
    if (!ending)
        return NS_ERROR_NULL_POINTER;

    // ensure that src string doesn't contain any ','s or ';'s
    p = PL_strdup(src);
    for ( i=0; p[i]; i++ ) {
        if (p[i] == ',')
            p[i] = '~';  // Replace ','s with '~'s
        else if (p[i] == ';')
            p[i] = '|';  // Replace ';'s with '|'s

    }


    PL_strcat(*dest, p);
    PL_strcat(*dest, ending);
    PR_FREEIF(p);

    return NS_OK;
}

Here is the call graph for this function:

unsigned char * scale_image ( unsigned char *  rgb_data,
int  rgb_width,
int  rgb_height,
int  w,
int  h 
) [static]

Definition at line 2248 of file nsSanePlugin.cpp.

{
  int                 x, y, *xarray;
  unsigned char     **yarray, *ptr, *ptr2, *ptr22, *new_data;
  int                 l, r, m, pos, inc, w3;

  new_data = (unsigned char *) PR_Malloc( w * h * 3);
  if (!new_data) {
    NS_ERROR("ERROR: Cannot allocate image buffer\n");
    return NULL;
  }

  xarray = (int *)PR_Malloc(sizeof(int) * w);
  if (!xarray) {
    NS_ERROR("ERROR: Cannot allocate X co-ord buffer\n");
    PR_FREEIF(new_data);
    return NULL;
  }

  yarray = (unsigned char **)PR_Malloc(sizeof(unsigned char *) * h);
  if (!yarray) {
    NS_ERROR("ERROR: Cannot allocate Y co-ord buffer\n");
    PR_FREEIF(new_data);
    PR_FREEIF(xarray);
    return NULL;
  }
  
  ptr22 = rgb_data;
  w3 = rgb_width * 3;
  inc = 0;
  l = 0;
  r = 0;
  m = w - l - r;

  inc = (rgb_width << 16) / m;
  pos = 0;

  for (x = l; x < l + m; x++) {
    xarray[x] = (pos >> 16) + (pos >> 16) + (pos >> 16);
    pos += inc;
  }

  pos = (rgb_width - r) << 16;
  for (x = w - r; x < w; x++) {
    xarray[x] = (pos >> 16) + (pos >> 16) + (pos >> 16);
    pos += 0x10000;
  }

  l = 0;
  r = 0;
  m = h - l - r;

  if (m > 0)
    inc = (rgb_height << 16) / m;

  pos = 0;

  for (x = l; x < l + m; x++) {
    yarray[x] = ptr22 + ((pos >> 16) * w3);
    pos += inc;
  }

  pos = (rgb_height - r) << 16;
  for (x = h - r; x < h; x++) {
    yarray[x] = ptr22 + ((pos >> 16) * w3);
    pos += 0x10000;
  }

  ptr = new_data;
  for (y = 0; y < h; y++) {
    for (x = 0; x < w; x++) {
      ptr2 = yarray[y] + xarray[x];
      *ptr++ = (int)*ptr2++;
      *ptr++ = (int)*ptr2++;
      *ptr++ = (int)*ptr2;
    }
  }

  PR_FREEIF(xarray);
  PR_FREEIF(yarray);

  return new_data;
}

Here is the caller graph for this function:

Definition at line 2397 of file nsSanePlugin.cpp.

{
    nsSanePluginInstance * pthis = (nsSanePluginInstance *)arg;

    nsresult rv;
    SANE_Status status;
    SANE_Parameters param;
    int len;
    FILE * out;
    SANE_Byte * buf;
    JSAMPROW row_pointer[1];
    char *fname;

    // JPEG stuff
    struct jpeg_compress_struct cinfo;
       struct jpeg_error_mgr jerr;

       // Error handling
    jerr.error_exit = my_jpeg_error_exit; // fatal errors
       cinfo.err = jpeg_std_error(&jerr);

       jpeg_create_compress(&cinfo);

    rv = pthis->OpenSaneDeviceIF();
    if (rv != NS_OK)
        goto error_exit;

    // open image file
    fname = pthis->GetImageFilename();
    if (!fname)
        goto error_exit;
    out = fopen(fname, "wb");
    PR_FREEIF(fname);
    if (out == NULL) {
        NS_ERROR("Unable to open mImageFilename!\n");
        sane_cancel(pthis->mSaneHandle);
        goto error_exit;       
    }
    jpeg_stdio_dest(&cinfo, out);

    status = sane_start(pthis->mSaneHandle);
    if (status != SANE_STATUS_GOOD) {
        NS_ERROR("Error trying to start sane device!");
        goto error_exit;
    }

    status = sane_get_parameters(pthis->mSaneHandle, &param);
    if (status != SANE_STATUS_GOOD) {
        NS_ERROR("Error trying to get image parameters!\n");
        sane_cancel(pthis->mSaneHandle);
        goto error_exit;
    }
    cinfo.image_width = param.pixels_per_line;
    cinfo.image_height = param.lines;

    switch (param.format) {
    case SANE_FRAME_RED:
    case SANE_FRAME_GREEN:
    case SANE_FRAME_BLUE:
        // NOT Supported!
        NS_ERROR("Multiframe devices not supported!\n");
        sane_cancel(pthis->mSaneHandle);
        goto error_exit;
        break;
                
    case SANE_FRAME_RGB:
        cinfo.input_components = 3;
        cinfo.in_color_space = JCS_RGB;
        break;

    case SANE_FRAME_GRAY:
        cinfo.input_components = 1;
        cinfo.in_color_space = JCS_GRAYSCALE;
        break;
    }
    jpeg_set_defaults(&cinfo); 
    cinfo.dct_method = pthis->mCompMethod; 
    jpeg_set_quality(&cinfo, pthis->mCompQuality, FALSE);
    jpeg_start_compress(&cinfo, TRUE);

    buf = (SANE_Byte *)PR_MALLOC(param.bytes_per_line);
    if (!buf) {
        NS_ERROR("Error trying to allocate buffer!\n");
        jpeg_destroy_compress(&cinfo);
        fclose(out);
        sane_cancel(pthis->mSaneHandle);
        goto error_exit;
    }

    int total;
    while(1) {
        total = 0;
        while (total < param.bytes_per_line) {
            // Read in data from sane device
            status = sane_read(pthis->mSaneHandle, buf + total, 
                               param.bytes_per_line - total, 
                               &len);
            if (status != SANE_STATUS_GOOD) {
                if (status == SANE_STATUS_EOF)
                    // done with this frame
                    goto finished_scan;
                else {
                    NS_ERROR("Error trying to read from sane device!\n");
                    PR_FREEIF(buf);
                    jpeg_destroy_compress(&cinfo);
                    fclose(out);
                    sane_cancel(pthis->mSaneHandle);
                    goto error_exit;
                }
            }
            total += len;
        }

        row_pointer[0] = (JSAMPLE *) buf;
        jpeg_write_scanlines(&cinfo, row_pointer, 1); 

    } // end while

    // cleanup
 finished_scan:
    jpeg_finish_compress(&cinfo);
    jpeg_destroy_compress(&cinfo);
    PR_FREEIF(buf);
    sane_cancel(pthis->mSaneHandle);
    fclose(out);
    pthis->SetSuccess(PR_TRUE); // allow caller to check for success

 error_exit:

    // Call onScanComplete callback in the UI thread

    nsCOMPtr<nsIEventQueue> eventQ;

    // Get the event queue of the current thread...
    nsCOMPtr<nsIEventQueueService> eventQService = 
             do_GetService(kEventQueueService, &rv);
    if (NS_FAILED(rv)) {
        NS_ERROR("Unable to get event queue service!\n");
        return;
    }

    rv = eventQService->GetThreadEventQueue(pthis->mUIThread,
                                            getter_AddRefs(eventQ));
    if (NS_FAILED(rv)) {
        NS_ERROR("Unable to get thread queue!\n");
        return;
    }

    PLEvent *event = new PLEvent;
    if (!event) {
        NS_ERROR("Unable to create new event!\n");
        return;
    }

    // ThreadHandleEvent will now execute in the UI thread.
    PL_InitEvent(event, 
                 pthis,
                 (PLHandleEventProc)  ThreadedHandleEvent,
                 (PLDestroyEventProc) ThreadedDestroyEvent);

    if (NS_FAILED(eventQ->PostEvent(event))) {
        NS_ERROR("Error trying to post event!\n");
        PL_DestroyEvent(event);
        return;
    }
}

Here is the call graph for this function:

void store_filename ( GtkFileSelection *  selector,
gpointer  user_data 
) [static]

Definition at line 2587 of file nsSanePlugin.cpp.

{
    nsSanePluginInstance *pthis = (nsSanePluginInstance *) user_data;    
    gchar *cmd;
    GtkWidget * fs;
    char * orig_filename;
    char * dest_filename;

    if (!user_data || !selector)
        return;

    fs = pthis->GetFileSelection();
    orig_filename = pthis->GetImageFilename();
    if ( !fs || !orig_filename )
        return;

    dest_filename = gtk_file_selection_get_filename (GTK_FILE_SELECTION(fs));
    if ( !dest_filename )
        return;

#ifdef DEBUG
    printf("Saving %s to %s\n", orig_filename, dest_filename);
#endif

    /*
     * I'm just doing a simple 'cp' command with the shell.
     * This probobly should be done in a NSPR safe kind
     * of way.
     */

    // cp /tmp/nsSanePlugin.###.jpg /path/to/save/to.jpg
    cmd = (gchar *)PR_Malloc(PL_strlen(orig_filename) + 
                             PL_strlen(dest_filename) + 6);
    if (!cmd)
        return;
        
    sprintf(cmd, "cp %s %s", orig_filename, dest_filename);
    system(cmd);

    PR_FREEIF(cmd);
}

Here is the call graph for this function:

void PR_CALLBACK ThreadedDestroyEvent ( PLEvent aEvent) [static]

Definition at line 2582 of file nsSanePlugin.cpp.

{
    delete aEvent;
}

Here is the caller graph for this function:

void PR_CALLBACK ThreadedHandleEvent ( PLEvent aEvent) [static]

Definition at line 2565 of file nsSanePlugin.cpp.

{
    nsSanePluginInstance *pthis = (nsSanePluginInstance *)event->owner;    
   
    // If this isn't the UI's thread then we are hosed!
    if (PR_FALSE == pthis->IsUIThread()) {
        NS_ERROR("Attempt to execute event from the wrong thread!\n");
        return;
    }

    pthis->SetState(0); // no longer scanning
    pthis->Restore();  // repaint image

    // Notify user that routine is finished
    pthis->DoScanCompleteCallback();
}

Here is the call graph for this function:

Here is the caller graph for this function:


Variable Documentation

Definition at line 95 of file nsSanePlugin.cpp.