summaryrefslogtreecommitdiffstats
path: root/third_party/jpeg-xl/lib/extras/dec/decode.cc
blob: 3546cb65c03800abfd3cf52f51a6d0a8d77c589f (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
// 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.

#include "lib/extras/dec/decode.h"

#include <locale>

#include "lib/extras/dec/apng.h"
#include "lib/extras/dec/exr.h"
#include "lib/extras/dec/gif.h"
#include "lib/extras/dec/jpg.h"
#include "lib/extras/dec/jxl.h"
#include "lib/extras/dec/pgx.h"
#include "lib/extras/dec/pnm.h"

namespace jxl {
namespace extras {
namespace {

// Any valid encoding is larger (ensures codecs can read the first few bytes)
constexpr size_t kMinBytes = 9;

std::string GetExtension(const std::string& path) {
  // Pattern: "name.png"
  size_t pos = path.find_last_of('.');
  if (pos != std::string::npos) {
    return path.substr(pos);
  }

  // Extension not found
  return "";
}

}  // namespace

Codec CodecFromPath(const std::string& path,
                    size_t* JXL_RESTRICT bits_per_sample,
                    std::string* extension) {
  std::string ext = GetExtension(path);
  if (extension) {
    if (extension->empty()) {
      *extension = ext;
    } else {
      ext = *extension;
    }
  }
  std::transform(ext.begin(), ext.end(), ext.begin(), [](char c) {
    return std::tolower(c, std::locale::classic());
  });
  if (ext == ".png") return Codec::kPNG;

  if (ext == ".jpg") return Codec::kJPG;
  if (ext == ".jpeg") return Codec::kJPG;

  if (ext == ".pgx") return Codec::kPGX;

  if (ext == ".pam") return Codec::kPNM;
  if (ext == ".pnm") return Codec::kPNM;
  if (ext == ".pgm") return Codec::kPNM;
  if (ext == ".ppm") return Codec::kPNM;
  if (ext == ".pfm") {
    if (bits_per_sample != nullptr) *bits_per_sample = 32;
    return Codec::kPNM;
  }

  if (ext == ".gif") return Codec::kGIF;

  if (ext == ".exr") return Codec::kEXR;

  return Codec::kUnknown;
}

bool CanDecode(Codec codec) {
  switch (codec) {
    case Codec::kEXR:
      return CanDecodeEXR();
    case Codec::kGIF:
      return CanDecodeGIF();
    case Codec::kJPG:
      return CanDecodeJPG();
    case Codec::kPNG:
      return CanDecodeAPNG();
    case Codec::kPNM:
    case Codec::kPGX:
    case Codec::kJXL:
      return true;
    default:
      return false;
  }
}

Status DecodeBytes(const Span<const uint8_t> bytes,
                   const ColorHints& color_hints, extras::PackedPixelFile* ppf,
                   const SizeConstraints* constraints, Codec* orig_codec) {
  if (bytes.size() < kMinBytes) return JXL_FAILURE("Too few bytes");

  *ppf = extras::PackedPixelFile();

  // Default values when not set by decoders.
  ppf->info.uses_original_profile = JXL_TRUE;
  ppf->info.orientation = JXL_ORIENT_IDENTITY;

  const auto choose_codec = [&]() -> Codec {
    if (DecodeImageAPNG(bytes, color_hints, ppf, constraints)) {
      return Codec::kPNG;
    }
    if (DecodeImagePGX(bytes, color_hints, ppf, constraints)) {
      return Codec::kPGX;
    }
    if (DecodeImagePNM(bytes, color_hints, ppf, constraints)) {
      return Codec::kPNM;
    }
    JXLDecompressParams dparams = {};
    for (const uint32_t num_channels : {1, 2, 3, 4}) {
      dparams.accepted_formats.push_back(
          {num_channels, JXL_TYPE_FLOAT, JXL_LITTLE_ENDIAN, /*align=*/0});
    }
    dparams.output_bitdepth.type = JXL_BIT_DEPTH_FROM_CODESTREAM;
    size_t decoded_bytes;
    if (DecodeImageJXL(bytes.data(), bytes.size(), dparams, &decoded_bytes,
                       ppf) &&
        ApplyColorHints(color_hints, true, ppf->info.num_color_channels == 1,
                        ppf)) {
      return Codec::kJXL;
    }
    if (DecodeImageGIF(bytes, color_hints, ppf, constraints)) {
      return Codec::kGIF;
    }
    if (DecodeImageJPG(bytes, color_hints, ppf, constraints)) {
      return Codec::kJPG;
    }
    if (DecodeImageEXR(bytes, color_hints, ppf, constraints)) {
      return Codec::kEXR;
    }
    return Codec::kUnknown;
  };

  Codec codec = choose_codec();
  if (codec == Codec::kUnknown) {
    return JXL_FAILURE("Codecs failed to decode");
  }
  if (orig_codec) *orig_codec = codec;

  return true;
}

}  // namespace extras
}  // namespace jxl