1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "error_codes.h"
18#include "jni_defines.h"
19#include "jpeg_hook.h"
20
21#include <stddef.h>
22#include <string.h>
23
24void Mgr_init_destination_fcn(j_compress_ptr cinfo) {
25    DestManager *dst = reinterpret_cast<DestManager*>(cinfo->dest);
26    dst->mgr.next_output_byte = reinterpret_cast<JOCTET*>(dst->outStream->getBufferPtr());
27    dst->mgr.free_in_buffer = dst->outStream->getBufferSize();
28}
29
30boolean Mgr_empty_output_buffer_fcn(j_compress_ptr cinfo) {
31    DestManager *dst = reinterpret_cast<DestManager*>(cinfo->dest);
32    int32_t len = dst->outStream->getBufferSize();
33    if (dst->outStream->write(len, 0) != J_SUCCESS) {
34        ERREXIT(cinfo, JERR_FILE_WRITE);
35    }
36    dst->mgr.next_output_byte = reinterpret_cast<JOCTET*>(dst->outStream->getBufferPtr());
37    dst->mgr.free_in_buffer = len;
38    return TRUE;
39}
40
41void Mgr_term_destination_fcn(j_compress_ptr cinfo) {
42    DestManager *dst = reinterpret_cast<DestManager*>(cinfo->dest);
43    int32_t remaining = dst->outStream->getBufferSize() - dst->mgr.free_in_buffer;
44    if (dst->outStream->write(remaining, 0) != J_SUCCESS) {
45        ERREXIT(cinfo, JERR_FILE_WRITE);
46    }
47}
48
49int32_t MakeDst(j_compress_ptr cinfo, JNIEnv *env, jobject outStream) {
50    if (cinfo->dest != NULL) {
51        LOGE("DestManager already exists, cannot allocate!");
52        return J_ERROR_FATAL;
53    } else {
54        size_t size = sizeof(DestManager);
55        cinfo->dest = (struct jpeg_destination_mgr *) (*cinfo->mem->alloc_small)
56                ((j_common_ptr) cinfo, JPOOL_PERMANENT, size);
57        if (cinfo->dest == NULL) {
58            LOGE("Could not allocate memory for DestManager.");
59            return J_ERROR_FATAL;
60        }
61        memset(cinfo->dest, '0', size);
62    }
63    DestManager *d = reinterpret_cast<DestManager*>(cinfo->dest);
64    d->mgr.init_destination = Mgr_init_destination_fcn;
65    d->mgr.empty_output_buffer = Mgr_empty_output_buffer_fcn;
66    d->mgr.term_destination = Mgr_term_destination_fcn;
67    d->outStream = new OutputStreamWrapper();
68    if(d->outStream->init(env, outStream)) {
69        return J_SUCCESS;
70    }
71    return J_ERROR_FATAL;
72}
73
74void UpdateDstEnv(j_compress_ptr cinfo, JNIEnv* env) {
75    DestManager* d = reinterpret_cast<DestManager*>(cinfo->dest);
76    d->outStream->updateEnv(env);
77}
78
79void CleanDst(j_compress_ptr cinfo) {
80    if (cinfo != NULL && cinfo->dest != NULL) {
81        DestManager *d = reinterpret_cast<DestManager*>(cinfo->dest);
82        if (d->outStream != NULL) {
83            delete d->outStream;
84            d->outStream = NULL;
85        }
86    }
87}
88
89boolean Mgr_fill_input_buffer_fcn(j_decompress_ptr cinfo) {
90    SourceManager *src = reinterpret_cast<SourceManager*>(cinfo->src);
91    int32_t bytesRead = src->inStream->read(src->inStream->getBufferSize(), 0);
92    if (bytesRead == J_DONE) {
93        if (src->start_of_file == TRUE) {
94            ERREXIT(cinfo, JERR_INPUT_EMPTY);
95        }
96        WARNMS(cinfo, JWRN_JPEG_EOF);
97        bytesRead = src->inStream->forceReadEOI();
98    } else if (bytesRead < 0) {
99        ERREXIT(cinfo, JERR_FILE_READ);
100    } else if (bytesRead == 0) {
101        LOGW("read 0 bytes from InputStream.");
102    }
103    src->mgr.next_input_byte = reinterpret_cast<JOCTET*>(src->inStream->getBufferPtr());
104    src->mgr.bytes_in_buffer = bytesRead;
105    if (bytesRead != 0) {
106        src->start_of_file = FALSE;
107    }
108    return TRUE;
109}
110
111void Mgr_init_source_fcn(j_decompress_ptr cinfo) {
112    SourceManager *s = reinterpret_cast<SourceManager*>(cinfo->src);
113    s->start_of_file = TRUE;
114    Mgr_fill_input_buffer_fcn(cinfo);
115}
116
117void Mgr_skip_input_data_fcn(j_decompress_ptr cinfo, long num_bytes) {
118    // Cannot skip negative or 0 bytes.
119    if (num_bytes <= 0) {
120        LOGW("skipping 0 bytes in InputStream");
121        return;
122    }
123    SourceManager *src = reinterpret_cast<SourceManager*>(cinfo->src);
124    if (src->mgr.bytes_in_buffer >= num_bytes) {
125        src->mgr.bytes_in_buffer -= num_bytes;
126        src->mgr.next_input_byte += num_bytes;
127    } else {
128        // if skipping more bytes than remain in buffer, set skip_bytes
129        int64_t skip = num_bytes - src->mgr.bytes_in_buffer;
130        src->mgr.next_input_byte += src->mgr.bytes_in_buffer;
131        src->mgr.bytes_in_buffer = 0;
132        int64_t actual = src->inStream->skip(skip);
133        if (actual < 0) {
134            ERREXIT(cinfo, JERR_FILE_READ);
135        }
136        skip -= actual;
137        while (skip > 0) {
138            actual = src->inStream->skip(skip);
139            if (actual < 0) {
140                ERREXIT(cinfo, JERR_FILE_READ);
141            }
142            skip -= actual;
143            if (actual == 0) {
144                // Multiple zero byte skips, likely EOF
145                WARNMS(cinfo, JWRN_JPEG_EOF);
146                return;
147            }
148        }
149    }
150}
151
152void Mgr_term_source_fcn(j_decompress_ptr cinfo) {
153    //noop
154}
155
156int32_t MakeSrc(j_decompress_ptr cinfo, JNIEnv *env, jobject inStream){
157    if (cinfo->src != NULL) {
158        LOGE("SourceManager already exists, cannot allocate!");
159        return J_ERROR_FATAL;
160    } else {
161        size_t size = sizeof(SourceManager);
162        cinfo->src = (struct jpeg_source_mgr *) (*cinfo->mem->alloc_small)
163                ((j_common_ptr) cinfo, JPOOL_PERMANENT, size);
164        if (cinfo->src == NULL) {
165            // Could not allocate memory.
166            LOGE("Could not allocate memory for SourceManager.");
167            return J_ERROR_FATAL;
168        }
169        memset(cinfo->src, '0', size);
170    }
171    SourceManager *s = reinterpret_cast<SourceManager*>(cinfo->src);
172    s->start_of_file = TRUE;
173    s->mgr.init_source = Mgr_init_source_fcn;
174    s->mgr.fill_input_buffer = Mgr_fill_input_buffer_fcn;
175    s->mgr.skip_input_data = Mgr_skip_input_data_fcn;
176    s->mgr.resync_to_restart = jpeg_resync_to_restart;  // use default restart
177    s->mgr.term_source = Mgr_term_source_fcn;
178    s->inStream = new InputStreamWrapper();
179    if(s->inStream->init(env, inStream)) {
180        return J_SUCCESS;
181    }
182    return J_ERROR_FATAL;
183}
184
185void UpdateSrcEnv(j_decompress_ptr cinfo, JNIEnv* env) {
186    SourceManager* s = reinterpret_cast<SourceManager*>(cinfo->src);
187    s->inStream->updateEnv(env);
188}
189
190void CleanSrc(j_decompress_ptr cinfo) {
191    if (cinfo != NULL && cinfo->src != NULL) {
192        SourceManager *s = reinterpret_cast<SourceManager*>(cinfo->src);
193        if (s->inStream != NULL) {
194            delete s->inStream;
195            s->inStream = NULL;
196        }
197    }
198}
199