#include "cartographer/io/image.h" #include #include "cartographer/io/file_writer.h" #include "glog/logging.h" namespace cartographer { namespace io { namespace { uint32 Uint8ColorToCairo(const Uint8Color& color) { return static_cast(255) << 24 | static_cast(color[0]) << 16 | static_cast(color[1]) << 8 | color[2]; } Uint8Color CairoToUint8Color(uint32 color) { uint8 r = color >> 16; uint8 g = color >> 8; uint8 b = color; return {{r, g, b}}; } cairo_status_t CairoWriteCallback(void* const closure, const unsigned char* data, const unsigned int length) { if (static_cast(closure)->Write( reinterpret_cast(data), length)) { return CAIRO_STATUS_SUCCESS; } return CAIRO_STATUS_WRITE_ERROR; } void CheckStrideIsAsExpected(int width) { const int stride = cairo_format_stride_for_width(kCairoFormat, width); CHECK_EQ(stride, width * 4); } } // namespace UniqueCairoSurfacePtr MakeUniqueCairoSurfacePtr(cairo_surface_t* surface) { return UniqueCairoSurfacePtr(surface, cairo_surface_destroy); } UniqueCairoPtr MakeUniqueCairoPtr(cairo_t* surface) { return UniqueCairoPtr(surface, cairo_destroy); } Image::Image(int width, int height) : width_(width), height_(height), pixels_(width * height, 0) {} Image::Image(UniqueCairoSurfacePtr surface) : width_(cairo_image_surface_get_width(surface.get())), height_(cairo_image_surface_get_height(surface.get())) { CHECK_EQ(cairo_image_surface_get_format(surface.get()), kCairoFormat); CheckStrideIsAsExpected(width_); const uint32* pixel_data = reinterpret_cast(cairo_image_surface_get_data(surface.get())); const int num_pixels = width_ * height_; pixels_.reserve(num_pixels); for (int i = 0; i < num_pixels; ++i) { pixels_.push_back(pixel_data[i]); } } void Image::Rotate90DegreesClockwise() { const auto old_pixels = pixels_; pixels_.clear(); for (int x = 0; x < width_; ++x) { for (int y = height_ - 1; y >= 0; --y) { pixels_.push_back(old_pixels.at(y * width_ + x)); } } std::swap(width_, height_); } void Image::WritePng(FileWriter* const file_writer) { // TODO(hrapp): cairo_image_surface_create_for_data does not take ownership of // the data until the surface is finalized. Once it is finalized though, // cairo_surface_write_to_png fails, complaining that the surface is already // finalized. This makes it pretty hard to pass back ownership of the image to // the caller. UniqueCairoSurfacePtr surface = GetCairoSurface(); CHECK_EQ(cairo_surface_status(surface.get()), CAIRO_STATUS_SUCCESS); CHECK_EQ(cairo_surface_write_to_png_stream(surface.get(), &CairoWriteCallback, file_writer), CAIRO_STATUS_SUCCESS); } const Uint8Color Image::GetPixel(int x, int y) const { return CairoToUint8Color(pixels_[y * width_ + x]); } void Image::SetPixel(int x, int y, const Uint8Color& color) { pixels_[y * width_ + x] = Uint8ColorToCairo(color); } UniqueCairoSurfacePtr Image::GetCairoSurface() { return MakeUniqueCairoSurfacePtr(cairo_image_surface_create_for_data( reinterpret_cast(pixels_.data()), kCairoFormat, width_, height_, width_ * 4 /* stride */)); } } // namespace io } // namespace cartographer