blob: 08c204ff360a7f18cc5fc6251d1380464ed89c3b [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 "./image_enc.h"
#include "src/dsp/dsp.h"
#include "src/utils/utils.h"
#include "src/utils/vector.h"
#include "src/utils/plane.h"
#include "src/utils/csp.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.format() == WP2_Argb_32, WP2_STATUS_UNSUPPORTED_FEATURE);
const uint32_t width = buffer.width();
const uint32_t height = buffer.height();
const uint8_t* row = buffer.GetRow8(0);
const uint32_t stride = buffer.stride();
const size_t bytes_per_px = alpha ? 4 : 3;
WP2SampleFormat fmt;
Vector_u8 dest;
uint32_t y;
WP2_CHECK_OK(row != nullptr, WP2_STATUS_NULL_PARAMETER);
WP2_CHECK_ALLOC_OK(dest.resize(width * bytes_per_px));
WP2ArgbConverterInit();
if (alpha) {
fprintf(fout,
"P7\nWIDTH %u\nHEIGHT %u\nDEPTH 4\nMAXVAL 255\n"
"TUPLTYPE RGB_ALPHA\nENDHDR\n",
width, height);
fmt = WP2_RGBA_32;
} else {
fprintf(fout, "P6\n%u %u\n255\n", width, height);
fmt = WP2_RGB_24;
}
for (y = 0; y < height; ++y) {
WP2ArgbConvertTo[fmt](row, width, dest.data());
WP2_CHECK_OK(fwrite(dest.data(), width, bytes_per_px, fout) == bytes_per_px,
WP2_STATUS_BAD_WRITE);
row += stride;
}
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