aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/codec/SkJpegUtility.cpp
blob: 4eee28733679ef617a24b8fa0356fec78f1b1709 (plain)
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
/*
 * Copyright 2015 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkJpegUtility.h"

#include "SkCodecPriv.h"

/*
 * Call longjmp to continue execution on an error
 */
void skjpeg_err_exit(j_common_ptr dinfo) {
    // Simply return to Skia client code
    // JpegDecoderMgr will take care of freeing memory
    skjpeg_error_mgr* error = (skjpeg_error_mgr*) dinfo->err;
    (*error->output_message) (dinfo);
    if (error->fJmpBufStack.empty()) {
        SK_ABORT("JPEG error with no jmp_buf set.");
    }
    longjmp(*error->fJmpBufStack.back(), 1);
}

// Functions for buffered sources //

/*
 * Initialize the buffered source manager
 */
static void sk_init_buffered_source(j_decompress_ptr dinfo) {
    skjpeg_source_mgr* src = (skjpeg_source_mgr*) dinfo->src;
    src->next_input_byte = (const JOCTET*) src->fBuffer;
    src->bytes_in_buffer = 0;
}

/*
 * Fill the input buffer from the stream
 */
static boolean sk_fill_buffered_input_buffer(j_decompress_ptr dinfo) {
    skjpeg_source_mgr* src = (skjpeg_source_mgr*) dinfo->src;
    size_t bytes = src->fStream->read(src->fBuffer, skjpeg_source_mgr::kBufferSize);

    // libjpeg is still happy with a less than full read, as long as the result is non-zero
    if (bytes == 0) {
        // Let libjpeg know that the buffer needs to be refilled
        src->next_input_byte = nullptr;
        src->bytes_in_buffer = 0;
        return false;
    }

    src->next_input_byte = (const JOCTET*) src->fBuffer;
    src->bytes_in_buffer = bytes;
    return true;
}

/*
 * Skip a certain number of bytes in the stream
 */
static void sk_skip_buffered_input_data(j_decompress_ptr dinfo, long numBytes) {
    skjpeg_source_mgr* src = (skjpeg_source_mgr*) dinfo->src;
    size_t bytes = (size_t) numBytes;

    if (bytes > src->bytes_in_buffer) {
        size_t bytesToSkip = bytes - src->bytes_in_buffer;
        if (bytesToSkip != src->fStream->skip(bytesToSkip)) {
            SkCodecPrintf("Failure to skip.\n");
            dinfo->err->error_exit((j_common_ptr) dinfo);
            return;
        }

        src->next_input_byte = (const JOCTET*) src->fBuffer;
        src->bytes_in_buffer = 0;
    } else {
        src->next_input_byte += numBytes;
        src->bytes_in_buffer -= numBytes;
    }
}

/*
 * We do not need to do anything to terminate our stream
 */
static void sk_term_source(j_decompress_ptr dinfo)
{
    // The current implementation of SkJpegCodec does not call
    // jpeg_finish_decompress(), so this function is never called.
    // If we want to modify this function to do something, we also
    // need to modify SkJpegCodec to call jpeg_finish_decompress().
}

// Functions for memory backed sources //

/*
 * Initialize the mem backed source manager
 */
static void sk_init_mem_source(j_decompress_ptr dinfo) {
    /* no work necessary here, all things are done in constructor */
}

static void sk_skip_mem_input_data (j_decompress_ptr cinfo, long num_bytes) {
    jpeg_source_mgr* src = cinfo->src;
    size_t bytes = static_cast<size_t>(num_bytes);
    if(bytes > src->bytes_in_buffer) {
        src->next_input_byte = nullptr;
        src->bytes_in_buffer = 0;
    } else {
        src->next_input_byte += bytes;
        src->bytes_in_buffer -= bytes;
    }
}

static boolean sk_fill_mem_input_buffer (j_decompress_ptr cinfo) {
    /* The whole JPEG data is expected to reside in the supplied memory,
     * buffer, so any request for more data beyond the given buffer size
     * is treated as an error.
     */
    return false;
}

/*
 * Constructor for the source manager that we provide to libjpeg
 * We provide skia implementations of all of the stream processing functions required by libjpeg
 */
skjpeg_source_mgr::skjpeg_source_mgr(SkStream* stream)
    : fStream(stream)
{
    if (stream->hasLength() && stream->getMemoryBase()) {
        init_source = sk_init_mem_source;
        fill_input_buffer = sk_fill_mem_input_buffer;
        skip_input_data = sk_skip_mem_input_data;
        resync_to_restart = jpeg_resync_to_restart;
        term_source = sk_term_source;
        bytes_in_buffer = static_cast<size_t>(stream->getLength());
        next_input_byte = static_cast<const JOCTET*>(stream->getMemoryBase());
    } else {
        init_source = sk_init_buffered_source;
        fill_input_buffer = sk_fill_buffered_input_buffer;
        skip_input_data = sk_skip_buffered_input_data;
        resync_to_restart = jpeg_resync_to_restart;
        term_source = sk_term_source;
    }
}