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 218 219 220 221 222 223 224 225 226
|
/*
* Copyright 2016-2021 Esri
*
* Author: Lucian Plesea
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* JPNG band, uses JPEG or PNG encoding, depending on the input data
*/
#include "marfa.h"
CPL_C_START
#include <jpeglib.h>
#include <png.h>
CPL_C_END
NAMESPACE_MRF_START
// Test that all alpha values are equal to N
template <int N> static bool AllAlpha(const buf_mgr &src, const ILImage &img)
{
int stride = img.pagesize.c;
char *s = src.buffer + img.pagesize.c - 1;
char *stop = src.buffer + img.pageSizeBytes;
while (s < stop && N == static_cast<unsigned char>(*s))
s += stride;
return s >= stop;
}
// Strip the alpha from an RGBA buffer, safe to use in place
static void RGBA2RGB(const char *start, const char *stop, char *target)
{
while (start < stop)
{
*target++ = *start++;
*target++ = *start++;
*target++ = *start++;
start++; // Skip the alpha
}
}
// Add opaque alpha to an RGB buffer, safe to use in place
// works from stop to start, the last parameter is the end of the source region
static void RGB2RGBA(const char *start, char *stop, const char *source_end)
{
while (start < stop)
{
*--stop = ~static_cast<char>(0);
*--stop = *--source_end;
*--stop = *--source_end;
*--stop = *--source_end;
}
}
// Strip the alpha from an Luma Alpha buffer, safe to use in place
static void LA2L(const char *start, const char *stop, char *target)
{
while (start < stop)
{
*target++ = *start++;
start++; // Skip the alpha
}
}
// Add opaque alpha to a Luma buffer, safe to use in place
// works from stop to start, the last parameter is the end of the source region
static void L2LA(const char *start, char *stop, const char *source_end)
{
while (start < stop)
{
*--stop = ~static_cast<char>(0);
*--stop = *--source_end;
}
}
static CPLErr initBuffer(buf_mgr &b)
{
b.buffer = (char *)(CPLMalloc(b.size));
if (b.buffer != nullptr)
return CE_None;
CPLError(CE_Failure, CPLE_OutOfMemory, "Allocating temporary JPNG buffer");
return CE_Failure;
}
CPLErr JPNG_Band::Decompress(buf_mgr &dst, buf_mgr &src)
{
const static GUInt32 PNG_SIG = 0x474e5089; // PNG 4CC code
CPLErr retval = CE_None;
ILImage image(img);
GUInt32 signature;
memcpy(&signature, src.buffer, sizeof(signature));
// test against an LSB signature
if (JPEG_Codec::IsJPEG(src))
{
image.pagesize.c -= 1;
JPEG_Codec codec(image);
// JPEG decoder expects the destination size to be accurate
buf_mgr temp = dst; // dst still owns the storage
temp.size = (image.pagesize.c == 3) ? dst.size / 4 * 3 : dst.size / 2;
retval = codec.DecompressJPEG(temp, src);
if (CE_None == retval)
{ // add opaque alpha, in place
if (image.pagesize.c == 3)
RGB2RGBA(dst.buffer, dst.buffer + dst.size,
temp.buffer + temp.size);
else
L2LA(dst.buffer, dst.buffer + dst.size,
temp.buffer + temp.size);
}
}
else if (PNG_SIG == CPL_LSBWORD32(signature))
{ // Should be PNG, it reads as 4 bands
PNG_Codec codec(image);
return codec.DecompressPNG(dst, src);
}
else
{
CPLError(CE_Failure, CPLE_NotSupported, "Not a JPEG or PNG tile");
retval = CE_Failure;
}
return retval;
}
// The PNG internal palette is set on first band write
CPLErr JPNG_Band::Compress(buf_mgr &dst, buf_mgr &src)
{
ILImage image(img);
buf_mgr temp = {nullptr, static_cast<size_t>(img.pageSizeBytes)};
CPLErr retval = initBuffer(temp);
if (retval != CE_None)
return retval;
if (AllAlpha<255>(src, image))
{ // If all pixels are opaque, compress as JPEG
if (image.pagesize.c == 4)
RGBA2RGB(src.buffer, src.buffer + src.size, temp.buffer);
else
LA2L(src.buffer, src.buffer + src.size, temp.buffer);
image.pagesize.c -= 1; // RGB or Grayscale only for JPEG
JPEG_Codec codec(image);
codec.rgb = rgb;
codec.optimize = optimize;
codec.sameres = sameres;
codec.JFIF = JFIF;
retval = codec.CompressJPEG(dst, temp);
}
else if (!AllAlpha<0>(src, image))
{
PNG_Codec codec(image);
codec.deflate_flags = deflate_flags;
retval = codec.CompressPNG(dst, src);
}
else
dst.size = 0; // Don't store fully transparent pages
CPLFree(temp.buffer);
return retval;
}
/**
* \brief For PPNG, builds the data structures needed to write the palette
* The presence of the PNGColors and PNGAlpha is used as a flag for PPNG only
*/
JPNG_Band::JPNG_Band(MRFDataset *pDS, const ILImage &image, int b, int level)
: MRFRasterBand(pDS, image, b, level), rgb(FALSE), sameres(FALSE),
optimize(false), JFIF(false)
{ // Check error conditions
if (image.dt != GDT_Byte)
{
CPLError(CE_Failure, CPLE_NotSupported,
"Data type not supported by MRF JPNG");
return;
}
if (image.order != IL_Interleaved ||
(image.pagesize.c != 4 && image.pagesize.c != 2))
{
CPLError(CE_Failure, CPLE_NotSupported,
"MRF JPNG can only handle 2 or 4 interleaved bands");
return;
}
if (img.pagesize.c == 4)
{ // RGBA can have storage flavors
CPLString const &pm = pDS->GetPhotometricInterpretation();
if (pm == "RGB" || pm == "MULTISPECTRAL")
{ // Explicit RGB or MS
rgb = TRUE;
sameres = TRUE;
}
if (pm == "YCC")
sameres = TRUE;
}
optimize = GetOptlist().FetchBoolean("OPTIMIZE", FALSE) != FALSE;
JFIF = GetOptlist().FetchBoolean("JFIF", FALSE) != FALSE;
// PNGs and JPGs can be larger than the source, especially for
// small page size.
poMRFDS->SetPBufferSize(image.pageSizeBytes + 100);
}
JPNG_Band::~JPNG_Band()
{
}
NAMESPACE_MRF_END
|