Line data Source code
1 : /*
2 : * Copyright 2016-2021 Esri
3 : *
4 : * Author: Lucian Plesea
5 : *
6 : * Licensed under the Apache License, Version 2.0 (the "License");
7 : * you may not use this file except in compliance with the License.
8 : * You may obtain a copy of the License at
9 : *
10 : * http://www.apache.org/licenses/LICENSE-2.0
11 : *
12 : * Unless required by applicable law or agreed to in writing, software
13 : * distributed under the License is distributed on an "AS IS" BASIS,
14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 : * See the License for the specific language governing permissions and
16 : * limitations under the License.
17 : */
18 :
19 : /*
20 : * JPNG band, uses JPEG or PNG encoding, depending on the input data
21 : */
22 :
23 : #include "marfa.h"
24 :
25 : CPL_C_START
26 : #include <jpeglib.h>
27 : #include <png.h>
28 : CPL_C_END
29 :
30 : NAMESPACE_MRF_START
31 :
32 : // Test that all alpha values are equal to N
33 0 : template <int N> static bool AllAlpha(const buf_mgr &src, const ILImage &img)
34 : {
35 0 : int stride = img.pagesize.c;
36 0 : char *s = src.buffer + img.pagesize.c - 1;
37 0 : char *stop = src.buffer + img.pageSizeBytes;
38 0 : while (s < stop && N == static_cast<unsigned char>(*s))
39 0 : s += stride;
40 0 : return s >= stop;
41 : }
42 :
43 : // Strip the alpha from an RGBA buffer, safe to use in place
44 0 : static void RGBA2RGB(const char *start, const char *stop, char *target)
45 : {
46 0 : while (start < stop)
47 : {
48 0 : *target++ = *start++;
49 0 : *target++ = *start++;
50 0 : *target++ = *start++;
51 0 : start++; // Skip the alpha
52 : }
53 0 : }
54 :
55 : // Add opaque alpha to an RGB buffer, safe to use in place
56 : // works from stop to start, the last parameter is the end of the source region
57 0 : static void RGB2RGBA(const char *start, char *stop, const char *source_end)
58 : {
59 0 : while (start < stop)
60 : {
61 0 : *--stop = ~static_cast<char>(0);
62 0 : *--stop = *--source_end;
63 0 : *--stop = *--source_end;
64 0 : *--stop = *--source_end;
65 : }
66 0 : }
67 :
68 : // Strip the alpha from an Luma Alpha buffer, safe to use in place
69 0 : static void LA2L(const char *start, const char *stop, char *target)
70 : {
71 0 : while (start < stop)
72 : {
73 0 : *target++ = *start++;
74 0 : start++; // Skip the alpha
75 : }
76 0 : }
77 :
78 : // Add opaque alpha to a Luma buffer, safe to use in place
79 : // works from stop to start, the last parameter is the end of the source region
80 0 : static void L2LA(const char *start, char *stop, const char *source_end)
81 : {
82 0 : while (start < stop)
83 : {
84 0 : *--stop = ~static_cast<char>(0);
85 0 : *--stop = *--source_end;
86 : }
87 0 : }
88 :
89 0 : static CPLErr initBuffer(buf_mgr &b)
90 : {
91 0 : b.buffer = (char *)(CPLMalloc(b.size));
92 0 : if (b.buffer != nullptr)
93 0 : return CE_None;
94 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "Allocating temporary JPNG buffer");
95 0 : return CE_Failure;
96 : }
97 :
98 0 : CPLErr JPNG_Band::Decompress(buf_mgr &dst, buf_mgr &src)
99 : {
100 : const static GUInt32 PNG_SIG = 0x474e5089; // PNG 4CC code
101 :
102 0 : CPLErr retval = CE_None;
103 0 : ILImage image(img);
104 : GUInt32 signature;
105 0 : memcpy(&signature, src.buffer, sizeof(signature));
106 :
107 : // test against an LSB signature
108 0 : if (JPEG_Codec::IsJPEG(src))
109 : {
110 0 : image.pagesize.c -= 1;
111 0 : JPEG_Codec codec(image);
112 :
113 : // JPEG decoder expects the destination size to be accurate
114 0 : buf_mgr temp = dst; // dst still owns the storage
115 0 : temp.size = (image.pagesize.c == 3) ? dst.size / 4 * 3 : dst.size / 2;
116 :
117 0 : retval = codec.DecompressJPEG(temp, src);
118 0 : if (CE_None == retval)
119 : { // add opaque alpha, in place
120 0 : if (image.pagesize.c == 3)
121 0 : RGB2RGBA(dst.buffer, dst.buffer + dst.size,
122 0 : temp.buffer + temp.size);
123 : else
124 0 : L2LA(dst.buffer, dst.buffer + dst.size,
125 0 : temp.buffer + temp.size);
126 : }
127 : }
128 0 : else if (PNG_SIG == CPL_LSBWORD32(signature))
129 : { // Should be PNG, it reads as 4 bands
130 0 : PNG_Codec codec(image);
131 0 : return codec.DecompressPNG(dst, src);
132 : }
133 : else
134 : {
135 0 : CPLError(CE_Failure, CPLE_NotSupported, "Not a JPEG or PNG tile");
136 0 : retval = CE_Failure;
137 : }
138 :
139 0 : return retval;
140 : }
141 :
142 : // The PNG internal palette is set on first band write
143 0 : CPLErr JPNG_Band::Compress(buf_mgr &dst, buf_mgr &src)
144 : {
145 0 : ILImage image(img);
146 0 : buf_mgr temp = {nullptr, static_cast<size_t>(img.pageSizeBytes)};
147 0 : CPLErr retval = initBuffer(temp);
148 0 : if (retval != CE_None)
149 0 : return retval;
150 :
151 0 : if (AllAlpha<255>(src, image))
152 : { // If all pixels are opaque, compress as JPEG
153 0 : if (image.pagesize.c == 4)
154 0 : RGBA2RGB(src.buffer, src.buffer + src.size, temp.buffer);
155 : else
156 0 : LA2L(src.buffer, src.buffer + src.size, temp.buffer);
157 :
158 0 : image.pagesize.c -= 1; // RGB or Grayscale only for JPEG
159 0 : JPEG_Codec codec(image);
160 0 : codec.rgb = rgb;
161 0 : codec.optimize = optimize;
162 0 : codec.sameres = sameres;
163 0 : codec.JFIF = JFIF;
164 0 : retval = codec.CompressJPEG(dst, temp);
165 : }
166 0 : else if (!AllAlpha<0>(src, image))
167 : {
168 0 : PNG_Codec codec(image);
169 0 : codec.deflate_flags = deflate_flags;
170 0 : retval = codec.CompressPNG(dst, src);
171 : }
172 : else
173 0 : dst.size = 0; // Don't store fully transparent pages
174 :
175 0 : CPLFree(temp.buffer);
176 0 : return retval;
177 : }
178 :
179 : /**
180 : * \brief For PPNG, builds the data structures needed to write the palette
181 : * The presence of the PNGColors and PNGAlpha is used as a flag for PPNG only
182 : */
183 :
184 0 : JPNG_Band::JPNG_Band(MRFDataset *pDS, const ILImage &image, int b, int level)
185 : : MRFRasterBand(pDS, image, b, level), rgb(FALSE), sameres(FALSE),
186 0 : optimize(false), JFIF(false)
187 : { // Check error conditions
188 0 : if (image.dt != GDT_Byte)
189 : {
190 0 : CPLError(CE_Failure, CPLE_NotSupported,
191 : "Data type not supported by MRF JPNG");
192 0 : return;
193 : }
194 0 : if (image.order != IL_Interleaved ||
195 0 : (image.pagesize.c != 4 && image.pagesize.c != 2))
196 : {
197 0 : CPLError(CE_Failure, CPLE_NotSupported,
198 : "MRF JPNG can only handle 2 or 4 interleaved bands");
199 0 : return;
200 : }
201 :
202 0 : if (img.pagesize.c == 4)
203 : { // RGBA can have storage flavors
204 0 : CPLString const &pm = pDS->GetPhotometricInterpretation();
205 0 : if (pm == "RGB" || pm == "MULTISPECTRAL")
206 : { // Explicit RGB or MS
207 0 : rgb = TRUE;
208 0 : sameres = TRUE;
209 : }
210 0 : if (pm == "YCC")
211 0 : sameres = TRUE;
212 : }
213 :
214 0 : optimize = GetOptlist().FetchBoolean("OPTIMIZE", FALSE) != FALSE;
215 0 : JFIF = GetOptlist().FetchBoolean("JFIF", FALSE) != FALSE;
216 :
217 : // PNGs and JPGs can be larger than the source, especially for
218 : // small page size.
219 0 : poMRFDS->SetPBufferSize(image.pageSizeBytes + 100);
220 : }
221 :
222 0 : JPNG_Band::~JPNG_Band()
223 : {
224 0 : }
225 :
226 : NAMESPACE_MRF_END
|