blob: b473b6705bc10770dfb0b377ae1e04692908433f [file] [log] [blame]
// Copyright 2019 Google LLC
//
// 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
//
// https://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.
// -----------------------------------------------------------------------------
//
// Image savers
#include <cstdint>
#include <cstdio>
#include "imageio/image_enc.h"
#include "src/dsp/math.h"
#include "src/utils/csp.h"
#include "src/utils/plane.h"
#include "src/utils/utils.h"
#include "src/utils/vector.h"
#include "src/wp2/base.h"
#include "src/wp2/format_constants.h"
namespace WP2 {
static WP2Status WritePPMPAM(const ArgbBuffer& buffer, int alpha,
FILE* const fout) {
WP2_CHECK_OK(fout != nullptr, WP2_STATUS_NULL_PARAMETER);
WP2_CHECK_OK(!buffer.IsEmpty(), WP2_STATUS_NULL_PARAMETER);
WP2_CHECK_OK(WP2Formatbpc(buffer.format()) == 8,
WP2_STATUS_INVALID_PARAMETER);
ArgbBuffer converted_row(alpha ? WP2_RGBA_32 : WP2_RGB_24);
const uint8_t bytes_per_px = WP2FormatBpp(converted_row.format());
if (alpha) {
fprintf(fout,
"P7\nWIDTH %u\nHEIGHT %u\nDEPTH 4\nMAXVAL 255\n"
"TUPLTYPE RGB_ALPHA\nENDHDR\n",
buffer.width(), buffer.height());
} else {
fprintf(fout, "P6\n%u %u\n255\n", buffer.width(), buffer.height());
}
for (uint32_t y = 0; y < buffer.height(); ++y) {
ArgbBuffer buffer_row(buffer.format());
WP2_CHECK_STATUS(buffer_row.SetView(buffer, {0, y, buffer.width(), 1}));
if (converted_row.format() == buffer_row.format()) {
WP2_CHECK_STATUS(converted_row.SetView(buffer_row));
} else {
WP2_CHECK_STATUS(converted_row.ConvertFrom(buffer_row));
}
WP2_CHECK_OK(fwrite(converted_row.GetRow8(0), bytes_per_px,
converted_row.width(), fout) == converted_row.width(),
WP2_STATUS_BAD_WRITE);
}
return WP2_STATUS_OK;
}
WP2Status WritePPM(const ArgbBuffer& buffer, FILE* const fout) {
return WritePPMPAM(buffer, 0, fout);
}
WP2Status WritePAM(const ArgbBuffer& buffer, FILE* const fout) {
return WritePPMPAM(buffer, 1, fout);
}
//------------------------------------------------------------------------------
// Raw PGM
static WP2Status SaveRow(const Plane16& src, uint32_t y, FILE* const out,
uint32_t shift, int32_t offset, Vector_u8& tmp) {
const uint32_t width = src.w_;
const int16_t* const row = src.Row(y);
WP2_CHECK_ALLOC_OK(tmp.resize(width));
for (uint32_t i = 0; i < width; ++i) {
tmp[i] = Clamp<int32_t>((row[i] >> shift) + offset, 0, 255);
}
WP2_CHECK_OK(fwrite(&tmp[0], width, 1, out) == 1, WP2_STATUS_BAD_WRITE);
return WP2_STATUS_OK;
}
WP2Status WritePGM(const ArgbBuffer& buffer, FILE* const fout,
const CSPTransform& transform) {
WP2_CHECK_OK(fout != nullptr, WP2_STATUS_NULL_PARAMETER);
YUVPlane yuva;
const bool has_alpha = buffer.HasTransparency();
const uint32_t width = buffer.width();
const uint32_t height = buffer.height();
WP2_CHECK_STATUS(yuva.Import(buffer, has_alpha, transform,
/*resize_if_needed=*/true));
fprintf(fout, "P5\n%u %u\n255\n", width * (has_alpha ? 4 : 3), height);
Vector_u8 tmp;
const BitDepth bit_depth = transform.GetYuvDepth();
const uint32_t shift = bit_depth.num_bits - kRgbBits;
const int32_t offset = bit_depth.is_signed ? 128 : 0;
for (uint32_t y = 0; y < height; ++y) {
WP2_CHECK_STATUS(SaveRow(yuva.Y, y, fout, shift, offset, tmp));
WP2_CHECK_STATUS(SaveRow(yuva.U, y, fout, shift, offset, tmp));
WP2_CHECK_STATUS(SaveRow(yuva.V, y, fout, shift, offset, tmp));
if (has_alpha) {
WP2_CHECK_STATUS(SaveRow(yuva.A, y, fout, 0, 0, tmp));
}
}
return WP2_STATUS_OK;
}
} // namespace WP2