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 
Mgr_init_destination_fcn(j_compress_ptr cinfo)24 void 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 
Mgr_empty_output_buffer_fcn(j_compress_ptr cinfo)30 boolean 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 
Mgr_term_destination_fcn(j_compress_ptr cinfo)41 void 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 
MakeDst(j_compress_ptr cinfo,JNIEnv * env,jobject outStream)49 int32_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 
UpdateDstEnv(j_compress_ptr cinfo,JNIEnv * env)74 void UpdateDstEnv(j_compress_ptr cinfo, JNIEnv* env) {
75     DestManager* d = reinterpret_cast<DestManager*>(cinfo->dest);
76     d->outStream->updateEnv(env);
77 }
78 
CleanDst(j_compress_ptr cinfo)79 void 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 
Mgr_fill_input_buffer_fcn(j_decompress_ptr cinfo)89 boolean 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 
Mgr_init_source_fcn(j_decompress_ptr cinfo)111 void 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 
Mgr_skip_input_data_fcn(j_decompress_ptr cinfo,long num_bytes)117 void 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 >= (size_t)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 
Mgr_term_source_fcn(j_decompress_ptr cinfo __unused)152 void Mgr_term_source_fcn(j_decompress_ptr cinfo __unused) {
153     //noop
154 }
155 
MakeSrc(j_decompress_ptr cinfo,JNIEnv * env,jobject inStream)156 int32_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 
UpdateSrcEnv(j_decompress_ptr cinfo,JNIEnv * env)185 void UpdateSrcEnv(j_decompress_ptr cinfo, JNIEnv* env) {
186     SourceManager* s = reinterpret_cast<SourceManager*>(cinfo->src);
187     s->inStream->updateEnv(env);
188 }
189 
CleanSrc(j_decompress_ptr cinfo)190 void 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