about summary refs log tree commit diff
path: root/converter/other/jpegdatasource.c
blob: 34393a7bebc8c9d9d9d9daf1104bbaed7d4cd332 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
/*****************************************************************************
                           jpegdatasource.c
******************************************************************************
   This is the data source manager that Jpegtopnm supplies to the JPEG
   library.  The Jpeg library uses this object to get JPEG input.

   This data source manager is the same as the Jpeg library's built in
   "stdio" one, except that it looks ahead and one can query it to see
   if there is any data in the stream that the Jpeg library hasn't seen
   yet.  Thus, you can use it in a program that reads multiple JPEG
   images and know when to stop.  You can also nicely handle completely
   empty input files more gracefully than just crying input error.

   This data source manager does 4K fread() reads and passes 4K buffers
   to the Jpeg library.  It reads one 4K block ahead, so there is up to
   8K of image buffered at any time.

   By Bryan Henderson, San Jose CA 2002.10.13
*****************************************************************************/

#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
/* Note: jpeglib.h prerequires stdlib.h and ctype.h.  It should include them
   itself, but doesn't.
*/
#include <jpeglib.h>

#include "pm_c_util.h"
#include "mallocvar.h"
#include "pnm.h"

#include "jpegdatasource.h"

#define BUFFER_SIZE 4096

struct sourceManager {
    /* The following structure _must_ be the first thing in this structure,
       because the methods below assume the address of the struct
       jpeg_source_mgr is the same as the address of the struct sourceManager.
       The former address is the only information the Jpeg library gives
       these methods to tell them on what source manager object they are
       supposed to operate.
    */
    struct jpeg_source_mgr jpegSourceMgr;
    FILE * ifP;
    bool prematureEof;
        /* We have been asked for data and were unable to comply because
           the file had no more to give (so we supplied EOI markers
           instead).
        */
    JOCTET * currentBuffer;
    JOCTET * nextBuffer;
    unsigned int bytesInNextBuffer;
    JOCTET buffer1[BUFFER_SIZE];
    JOCTET buffer2[BUFFER_SIZE];
};


static void
dsInitSource(j_decompress_ptr const cinfoP) {
/*----------------------------------------------------------------------------
   This is the init_source method for the data source manager the Jpeg
   library uses.
-----------------------------------------------------------------------------*/
    /* The object was created ready to go and remains ready from one image
       to the next.  No per-image initialization is necessary.
    */
}



static const JOCTET jfifEoiMarker[] = {0xff, JPEG_EOI};
    /* An EOI (end of image) marker */


static boolean
dsFillInputBuffer(j_decompress_ptr const cinfoP) {
/*----------------------------------------------------------------------------
   This is the fill_input_buffer method for the data source manager the Jpeg
   library uses.
-----------------------------------------------------------------------------*/
    struct sourceManager * const srcP = (struct sourceManager *) cinfoP->src;

    if (srcP->bytesInNextBuffer == 0) {
        /* The decompressor expects more bytes, but there aren't any, so
           the file is corrupted -- probably truncated.  We want the
           decompressor to decompress whatever it's read so far, so we
           synthesize an EOI marker here, but we also set error state
           in the source manager.  The decompressor will recognize the
           truncation and pad out the image with gray.
        */
        srcP->prematureEof = TRUE;

        srcP->jpegSourceMgr.next_input_byte = jfifEoiMarker;
        srcP->jpegSourceMgr.bytes_in_buffer = sizeof(jfifEoiMarker);
    } else {
        /* Rotate the buffers */
        srcP->jpegSourceMgr.next_input_byte = srcP->nextBuffer;
        srcP->jpegSourceMgr.bytes_in_buffer = srcP->bytesInNextBuffer;
        {
            JOCTET * tmp;
            tmp = srcP->nextBuffer;
            srcP->nextBuffer = srcP->currentBuffer;
            srcP->currentBuffer = tmp;
        }

        /* Fill the new "next" buffer */
        srcP->bytesInNextBuffer =
            fread(srcP->nextBuffer, 1, BUFFER_SIZE, srcP->ifP);
    }
    return TRUE;
}



static void
dsSkipInputData(j_decompress_ptr const cinfoP, long const num_bytes) {
/*----------------------------------------------------------------------------
   This is the skip_input_data method for the data source manager the Jpeg
   library uses.
-----------------------------------------------------------------------------*/
    struct jpeg_source_mgr * const jpegSourceMgrP = cinfoP->src;

    long i;

    for (i = 0; i < num_bytes; ++i) {
        if (jpegSourceMgrP->bytes_in_buffer == 0)
            dsFillInputBuffer(cinfoP);
        ++jpegSourceMgrP->next_input_byte;
        --jpegSourceMgrP->bytes_in_buffer;
    }
}



static void
dsTermSource(j_decompress_ptr const cinfoP) {
/*----------------------------------------------------------------------------
   This is the term_source method for the data source manager the Jpeg
   library uses.
-----------------------------------------------------------------------------*/
    /* We couldn't care less that the Jpeg library is done reading an
       image.  The source manager object remains active and ready for the
       Jpeg library to read the next image.
    */
}



bool
dsDataLeft(struct sourceManager * const srcP) {

    return((srcP->jpegSourceMgr.bytes_in_buffer > 0 ||
            srcP->bytesInNextBuffer > 0));
}



bool
dsPrematureEof(struct sourceManager * const srcP) {

    return srcP->prematureEof;
}



struct sourceManager *
dsCreateSource(const char * const fileName) {

    struct sourceManager * srcP;

    MALLOCVAR(srcP);
    if (srcP == NULL)
        pm_error("Unable to get memory for the Jpeg library source manager.");

    srcP->ifP = pm_openr(fileName);

    srcP->jpegSourceMgr.init_source = dsInitSource;
    srcP->jpegSourceMgr.fill_input_buffer = dsFillInputBuffer;
    srcP->jpegSourceMgr.skip_input_data = dsSkipInputData;
    srcP->jpegSourceMgr.resync_to_restart = jpeg_resync_to_restart;
    srcP->jpegSourceMgr.term_source = dsTermSource;

    srcP->prematureEof = FALSE;
    srcP->currentBuffer = srcP->buffer1;
    srcP->nextBuffer = srcP->buffer2;
    srcP->jpegSourceMgr.bytes_in_buffer =
        fread(srcP->currentBuffer, 1, BUFFER_SIZE, srcP->ifP);
    srcP->jpegSourceMgr.next_input_byte = srcP->currentBuffer;
    srcP->bytesInNextBuffer =
        fread(srcP->nextBuffer, 1, BUFFER_SIZE, srcP->ifP);

    return srcP;
}



void
dsDestroySource(struct sourceManager * const srcP) {

    pm_close(srcP->ifP);
    free(srcP);

}



struct jpeg_source_mgr *
dsJpegSourceMgr(struct sourceManager * const srcP) {
    return &srcP->jpegSourceMgr;
}