1/*
2 * Copyright 2010 The Android Open Source Project
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8
9#include "SkJpegUtility.h"
10
11/////////////////////////////////////////////////////////////////////
12static void sk_init_source(j_decompress_ptr cinfo) {
13    skjpeg_source_mgr*  src = (skjpeg_source_mgr*)cinfo->src;
14    src->next_input_byte = (const JOCTET*)src->fBuffer;
15    src->bytes_in_buffer = 0;
16#ifdef SK_BUILD_FOR_ANDROID
17    src->current_offset = 0;
18#endif
19    if (!src->fStream->rewind()) {
20        SkDebugf("xxxxxxxxxxxxxx failure to rewind\n");
21        cinfo->err->error_exit((j_common_ptr)cinfo);
22    }
23}
24
25#ifdef SK_BUILD_FOR_ANDROID
26static boolean sk_seek_input_data(j_decompress_ptr cinfo, long byte_offset) {
27    skjpeg_source_mgr* src = (skjpeg_source_mgr*)cinfo->src;
28    size_t bo = (size_t) byte_offset;
29
30    if (bo > src->current_offset) {
31        (void)src->fStream->skip(bo - src->current_offset);
32    } else {
33        if (!src->fStream->rewind()) {
34            SkDebugf("xxxxxxxxxxxxxx failure to rewind\n");
35            cinfo->err->error_exit((j_common_ptr)cinfo);
36            return false;
37        }
38        (void)src->fStream->skip(bo);
39    }
40
41    src->current_offset = bo;
42    src->next_input_byte = (const JOCTET*)src->fBuffer;
43    src->bytes_in_buffer = 0;
44    return true;
45}
46#endif
47
48static boolean sk_fill_input_buffer(j_decompress_ptr cinfo) {
49    skjpeg_source_mgr* src = (skjpeg_source_mgr*)cinfo->src;
50    if (src->fDecoder != NULL && src->fDecoder->shouldCancelDecode()) {
51        return FALSE;
52    }
53    size_t bytes = src->fStream->read(src->fBuffer, skjpeg_source_mgr::kBufferSize);
54    // note that JPEG is happy with less than the full read,
55    // as long as the result is non-zero
56    if (bytes == 0) {
57        return FALSE;
58    }
59
60#ifdef SK_BUILD_FOR_ANDROID
61    src->current_offset += bytes;
62#endif
63    src->next_input_byte = (const JOCTET*)src->fBuffer;
64    src->bytes_in_buffer = bytes;
65    return TRUE;
66}
67
68static void sk_skip_input_data(j_decompress_ptr cinfo, long num_bytes) {
69    skjpeg_source_mgr*  src = (skjpeg_source_mgr*)cinfo->src;
70
71    if (num_bytes > (long)src->bytes_in_buffer) {
72        size_t bytesToSkip = num_bytes - src->bytes_in_buffer;
73        while (bytesToSkip > 0) {
74            size_t bytes = src->fStream->skip(bytesToSkip);
75            if (bytes <= 0 || bytes > bytesToSkip) {
76//              SkDebugf("xxxxxxxxxxxxxx failure to skip request %d returned %d\n", bytesToSkip, bytes);
77                cinfo->err->error_exit((j_common_ptr)cinfo);
78                return;
79            }
80#ifdef SK_BUILD_FOR_ANDROID
81            src->current_offset += bytes;
82#endif
83            bytesToSkip -= bytes;
84        }
85        src->next_input_byte = (const JOCTET*)src->fBuffer;
86        src->bytes_in_buffer = 0;
87    } else {
88        src->next_input_byte += num_bytes;
89        src->bytes_in_buffer -= num_bytes;
90    }
91}
92
93static void sk_term_source(j_decompress_ptr /*cinfo*/) {}
94
95
96///////////////////////////////////////////////////////////////////////////////
97
98skjpeg_source_mgr::skjpeg_source_mgr(SkStream* stream, SkImageDecoder* decoder)
99    : fStream(SkRef(stream))
100    , fDecoder(decoder) {
101
102    init_source = sk_init_source;
103    fill_input_buffer = sk_fill_input_buffer;
104    skip_input_data = sk_skip_input_data;
105    resync_to_restart = jpeg_resync_to_restart;
106    term_source = sk_term_source;
107#ifdef SK_BUILD_FOR_ANDROID
108    seek_input_data = sk_seek_input_data;
109#endif
110//    SkDebugf("**************** use memorybase %p %d\n", fMemoryBase, fMemoryBaseSize);
111}
112
113skjpeg_source_mgr::~skjpeg_source_mgr() {
114    SkSafeUnref(fStream);
115}
116
117///////////////////////////////////////////////////////////////////////////////
118
119static void sk_init_destination(j_compress_ptr cinfo) {
120    skjpeg_destination_mgr* dest = (skjpeg_destination_mgr*)cinfo->dest;
121
122    dest->next_output_byte = dest->fBuffer;
123    dest->free_in_buffer = skjpeg_destination_mgr::kBufferSize;
124}
125
126static boolean sk_empty_output_buffer(j_compress_ptr cinfo) {
127    skjpeg_destination_mgr* dest = (skjpeg_destination_mgr*)cinfo->dest;
128
129//  if (!dest->fStream->write(dest->fBuffer, skjpeg_destination_mgr::kBufferSize - dest->free_in_buffer))
130    if (!dest->fStream->write(dest->fBuffer,
131            skjpeg_destination_mgr::kBufferSize)) {
132        ERREXIT(cinfo, JERR_FILE_WRITE);
133        return false;
134    }
135
136    dest->next_output_byte = dest->fBuffer;
137    dest->free_in_buffer = skjpeg_destination_mgr::kBufferSize;
138    return TRUE;
139}
140
141static void sk_term_destination (j_compress_ptr cinfo) {
142    skjpeg_destination_mgr* dest = (skjpeg_destination_mgr*)cinfo->dest;
143
144    size_t size = skjpeg_destination_mgr::kBufferSize - dest->free_in_buffer;
145    if (size > 0) {
146        if (!dest->fStream->write(dest->fBuffer, size)) {
147            ERREXIT(cinfo, JERR_FILE_WRITE);
148            return;
149        }
150    }
151    dest->fStream->flush();
152}
153
154skjpeg_destination_mgr::skjpeg_destination_mgr(SkWStream* stream)
155        : fStream(stream) {
156    this->init_destination = sk_init_destination;
157    this->empty_output_buffer = sk_empty_output_buffer;
158    this->term_destination = sk_term_destination;
159}
160
161void skjpeg_error_exit(j_common_ptr cinfo) {
162    skjpeg_error_mgr* error = (skjpeg_error_mgr*)cinfo->err;
163
164    (*error->output_message) (cinfo);
165
166    /* Let the memory manager delete any temp files before we die */
167    jpeg_destroy(cinfo);
168
169    longjmp(error->fJmpBuf, -1);
170}
171