summaryrefslogtreecommitdiffstats
path: root/third_party/jpeg-xl/lib/jxl/decode_to_jpeg.h
blob: a64ace27a28554c705b7f1de5e8cd5987aad76a5 (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
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
217
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

#ifndef LIB_JXL_DECODE_TO_JPEG_H_
#define LIB_JXL_DECODE_TO_JPEG_H_

// JPEG XL to JPEG bytes decoder logic. The JxlToJpegDecoder class keeps track
// of the decoder state needed to parse the JPEG reconstruction box and provide
// the reconstructed JPEG to the output buffer.

#include <jxl/decode.h>
#include <stdint.h>
#include <stdlib.h>

#include <memory>
#include <vector>

#include "lib/jxl/base/status.h"
#include "lib/jxl/common.h"  // JPEGXL_ENABLE_TRANSCODE_JPEG
#include "lib/jxl/image_bundle.h"
#include "lib/jxl/jpeg/dec_jpeg_data.h"
#if JPEGXL_ENABLE_TRANSCODE_JPEG
#include "lib/jxl/jpeg/dec_jpeg_data_writer.h"
#endif  // JPEGXL_ENABLE_TRANSCODE_JPEG

namespace jxl {

#if JPEGXL_ENABLE_TRANSCODE_JPEG

class JxlToJpegDecoder {
 public:
  // Returns whether an output buffer is set.
  bool IsOutputSet() const { return next_out_ != nullptr; }

  // Returns whether the decoder is parsing a boxa JPEG box was parsed.
  bool IsParsingBox() const { return inside_box_; }

  // Sets the output buffer used when producing JPEG output.
  JxlDecoderStatus SetOutputBuffer(uint8_t* data, size_t size) {
    if (next_out_) return JXL_DEC_ERROR;
    next_out_ = data;
    avail_size_ = size;
    return JXL_DEC_SUCCESS;
  }

  // Releases the buffer set with SetOutputBuffer().
  size_t ReleaseOutputBuffer() {
    size_t result = avail_size_;
    next_out_ = nullptr;
    avail_size_ = 0;
    return result;
  }

  void StartBox(bool box_until_eof, size_t contents_size) {
    // A new box implies that we clear the buffer.
    buffer_.clear();
    inside_box_ = true;
    if (box_until_eof) {
      box_until_eof_ = true;
    } else {
      box_size_ = contents_size;
    }
  }

  // Consumes data from next_in/avail_in to reconstruct JPEG data.
  // Uses box_size_, inside_box_ and box_until_eof_ to calculate how much to
  // consume. Potentially stores unparsed data in buffer_.
  // Potentially populates jpeg_data_. Potentially updates inside_box_.
  // Returns JXL_DEC_JPEG_RECONSTRUCTION when finished, JXL_DEC_NEED_MORE_INPUT
  // if more input is needed, JXL_DEC_ERROR on parsing error.
  JxlDecoderStatus Process(const uint8_t** next_in, size_t* avail_in);

  // Returns non-owned copy of the JPEGData, only after Process finished and
  // the JPEGData was not yet moved to an image bundle with
  // SetImageBundleJpegData.
  jpeg::JPEGData* GetJpegData() { return jpeg_data_.get(); }

  // Returns how many exif or xmp app markers are present in the JPEG data. A
  // return value higher than 1 would require multiple exif boxes or multiple
  // xmp boxes in the container format, and this is not supported by the API and
  // considered an error. May only be called after Process returned success.
  static size_t NumExifMarkers(const jpeg::JPEGData& jpeg_data);
  static size_t NumXmpMarkers(const jpeg::JPEGData& jpeg_data);

  // Returns box content size for metadata, using the known data from the app
  // markers.
  static JxlDecoderStatus ExifBoxContentSize(const jpeg::JPEGData& jpeg_data,
                                             size_t* size);
  static JxlDecoderStatus XmlBoxContentSize(const jpeg::JPEGData& jpeg_data,
                                            size_t* size);

  // Returns JXL_DEC_ERROR if there is no exif/XMP marker or the data size
  // does not match, or this function is called before Process returned
  // success, JXL_DEC_SUCCESS otherwise. As input, provide the full box contents
  // but not the box header. In case of exif, this includes the 4-byte TIFF
  // header, even though it won't be copied into the JPEG.
  static JxlDecoderStatus SetExif(const uint8_t* data, size_t size,
                                  jpeg::JPEGData* jpeg_data);
  static JxlDecoderStatus SetXmp(const uint8_t* data, size_t size,
                                 jpeg::JPEGData* jpeg_data);

  // Sets the JpegData of the ImageBundle passed if there is anything to set.
  // Releases the JpegData from this decoder if set.
  Status SetImageBundleJpegData(ImageBundle* ib) {
    if (IsOutputSet() && jpeg_data_ != nullptr) {
      if (!jpeg::SetJPEGDataFromICC(ib->metadata()->color_encoding.ICC(),
                                    jpeg_data_.get())) {
        return false;
      }
      ib->jpeg_data.reset(jpeg_data_.release());
    }
    return true;
  }

  JxlDecoderStatus WriteOutput(const jpeg::JPEGData& jpeg_data) {
    // Copy JPEG bytestream if desired.
    uint8_t* tmp_next_out = next_out_;
    size_t tmp_avail_size = avail_size_;
    auto write = [&tmp_next_out, &tmp_avail_size](const uint8_t* buf,
                                                  size_t len) {
      size_t to_write = std::min<size_t>(tmp_avail_size, len);
      if (to_write != 0) memcpy(tmp_next_out, buf, to_write);
      tmp_next_out += to_write;
      tmp_avail_size -= to_write;
      return to_write;
    };
    Status write_result = jpeg::WriteJpeg(jpeg_data, write);
    if (!write_result) {
      if (tmp_avail_size == 0) {
        return JXL_DEC_JPEG_NEED_MORE_OUTPUT;
      }
      return JXL_DEC_ERROR;
    }
    next_out_ = tmp_next_out;
    avail_size_ = tmp_avail_size;
    return JXL_DEC_SUCCESS;
  }

 private:
  // Content of the most recently parsed JPEG reconstruction box if any.
  std::vector<uint8_t> buffer_;

  // Decoded content of the most recently parsed JPEG reconstruction box is
  // stored here.
  std::unique_ptr<jpeg::JPEGData> jpeg_data_;

  // True if the decoder is currently reading bytes inside a JPEG reconstruction
  // box.
  bool inside_box_ = false;

  // True if the JPEG reconstruction box had undefined size (all remaining
  // bytes).
  bool box_until_eof_ = false;
  // Size of most recently parsed JPEG reconstruction box contents.
  size_t box_size_ = 0;

  // Next bytes to write JPEG reconstruction to.
  uint8_t* next_out_ = nullptr;
  // Available bytes to write JPEG reconstruction to.
  size_t avail_size_ = 0;
};

#else

// Fake class that disables support for decoding JPEG XL to JPEG.
class JxlToJpegDecoder {
 public:
  bool IsOutputSet() const { return false; }
  bool IsParsingBox() const { return false; }

  JxlDecoderStatus SetOutputBuffer(uint8_t* /* data */, size_t /* size */) {
    return JXL_DEC_ERROR;
  }
  size_t ReleaseOutputBuffer() { return 0; }

  void StartBox(bool /* box_until_eof */, size_t /* contents_size */) {}

  JxlDecoderStatus Process(const uint8_t** next_in, size_t* avail_in) {
    return JXL_DEC_ERROR;
  }
  jpeg::JPEGData* GetJpegData() { return nullptr; }

  Status SetImageBundleJpegData(ImageBundle* /* ib */) { return true; }

  static size_t NumExifMarkers(const jpeg::JPEGData& /*jpeg_data*/) {
    return 0;
  }
  static size_t NumXmpMarkers(const jpeg::JPEGData& /*jpeg_data*/) { return 0; }
  static size_t ExifBoxContentSize(const jpeg::JPEGData& /*jpeg_data*/,
                                   size_t* /*size*/) {
    return JXL_DEC_ERROR;
  }
  static size_t XmlBoxContentSize(const jpeg::JPEGData& /*jpeg_data*/,
                                  size_t* /*size*/) {
    return JXL_DEC_ERROR;
  }
  static JxlDecoderStatus SetExif(const uint8_t* /*data*/, size_t /*size*/,
                                  jpeg::JPEGData* /*jpeg_data*/) {
    return JXL_DEC_ERROR;
  }
  static JxlDecoderStatus SetXmp(const uint8_t* /*data*/, size_t /*size*/,
                                 jpeg::JPEGData* /*jpeg_data*/) {
    return JXL_DEC_ERROR;
  }

  JxlDecoderStatus WriteOutput(const jpeg::JPEGData& /* jpeg_data */) {
    return JXL_DEC_SUCCESS;
  }
};

#endif  // JPEGXL_ENABLE_TRANSCODE_JPEG

}  // namespace jxl

#endif  // LIB_JXL_DECODE_TO_JPEG_H_