| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /* | ||
| 2 | * Copyright (c) 2000-2022 Inria | ||
| 3 | * All rights reserved. | ||
| 4 | * | ||
| 5 | * Redistribution and use in source and binary forms, with or without | ||
| 6 | * modification, are permitted provided that the following conditions are met: | ||
| 7 | * | ||
| 8 | * * Redistributions of source code must retain the above copyright notice, | ||
| 9 | * this list of conditions and the following disclaimer. | ||
| 10 | * * Redistributions in binary form must reproduce the above copyright notice, | ||
| 11 | * this list of conditions and the following disclaimer in the documentation | ||
| 12 | * and/or other materials provided with the distribution. | ||
| 13 | * * Neither the name of the ALICE Project-Team nor the names of its | ||
| 14 | * contributors may be used to endorse or promote products derived from this | ||
| 15 | * software without specific prior written permission. | ||
| 16 | * | ||
| 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
| 18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
| 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE | ||
| 21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
| 22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
| 23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||
| 24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||
| 25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
| 26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
| 27 | * POSSIBILITY OF SUCH DAMAGE. | ||
| 28 | * | ||
| 29 | * Contact: Bruno Levy | ||
| 30 | * | ||
| 31 | * https://www.inria.fr/fr/bruno-levy | ||
| 32 | * | ||
| 33 | * Inria, | ||
| 34 | * Domaine de Voluceau, | ||
| 35 | * 78150 Le Chesnay - Rocquencourt | ||
| 36 | * FRANCE | ||
| 37 | * | ||
| 38 | */ | ||
| 39 | |||
| 40 | #ifndef H_OGF_IMAGE_IO_IMAGE_RASTERIZER_H | ||
| 41 | #define H_OGF_IMAGE_IO_IMAGE_RASTERIZER_H | ||
| 42 | |||
| 43 | #include <geogram/basic/common.h> | ||
| 44 | #include <geogram/image/image.h> | ||
| 45 | |||
| 46 | /** | ||
| 47 | * \file geogram/image/image_serializer.h | ||
| 48 | * \brief Class to draw triangles in an image. | ||
| 49 | */ | ||
| 50 | |||
| 51 | namespace GEO { | ||
| 52 | |||
| 53 | /** | ||
| 54 | * \brief Draws triangles in an image. | ||
| 55 | */ | ||
| 56 | class GEOGRAM_API ImageRasterizer { | ||
| 57 | public: | ||
| 58 | |||
| 59 | /** | ||
| 60 | * \brief ImageRasterizer constructor. | ||
| 61 | * \param[in] image a pointer to the target image. | ||
| 62 | */ | ||
| 63 | ImageRasterizer(Image* image); | ||
| 64 | |||
| 65 | /** | ||
| 66 | * \brief Clears the target image. | ||
| 67 | */ | ||
| 68 | void clear(); | ||
| 69 | |||
| 70 | /** | ||
| 71 | * \brief Draws a triangle in the target image. | ||
| 72 | * \details Colors are linearly interpolated (Gouraud shading). | ||
| 73 | * No clipping is done. It is the responsibility of the | ||
| 74 | * caller to give coordinates in the viewport. | ||
| 75 | * \param[in] P1 , P2 , P3 the three vertices of the triangle, | ||
| 76 | * integer coordinates are in [0..width-1]x[0..height-1], where width | ||
| 77 | * and height are the sizes of the target image. | ||
| 78 | * \param[in] c1 , c2 , c3 the three colors of the vertices. | ||
| 79 | */ | ||
| 80 | void triangle( | ||
| 81 | const vec2i& P1, const Color& c1, | ||
| 82 | const vec2i& P2, const Color& c2, | ||
| 83 | const vec2i& P3, const Color& c3 | ||
| 84 | ); | ||
| 85 | |||
| 86 | |||
| 87 | /** | ||
| 88 | * \brief Draws a triangle in the target image. | ||
| 89 | * \details Colors are linearly interpolated (Gouraud shading). | ||
| 90 | * No clipping is done. It is the responsibility of the | ||
| 91 | * caller to give coordinates in the viewport. | ||
| 92 | * \param[in] p1 , p2 , p3 the three vertices of the triangle, | ||
| 93 | * coordinates are in the [0,1]x[0,1] square. | ||
| 94 | * \param[in] c1 , c2 , c3 the three colors of the vertices. | ||
| 95 | */ | ||
| 96 | ✗ | void triangle( | |
| 97 | const vec2& p1, const Color& c1, | ||
| 98 | const vec2& p2, const Color& c2, | ||
| 99 | const vec2& p3, const Color& c3 | ||
| 100 | ) { | ||
| 101 | ✗ | triangle( | |
| 102 | ✗ | transform(p1),c1, | |
| 103 | ✗ | transform(p2),c2, | |
| 104 | ✗ | transform(p3),c3 | |
| 105 | ); | ||
| 106 | ✗ | } | |
| 107 | |||
| 108 | /** | ||
| 109 | * \brief Draws a segment in the target image. | ||
| 110 | * \details No clipping is done. It is the responsibility of the | ||
| 111 | * caller to give valid coordinates. | ||
| 112 | * \param[in] P1 , P2 the two extremities of the segment. | ||
| 113 | * integer coordinates are in [0..width-1]x[0..height-1], where width | ||
| 114 | * and height are the sizes of the target image. | ||
| 115 | * \param[in] c the color | ||
| 116 | */ | ||
| 117 | void segment(const vec2i& P1, const vec2i& P2, const Color& c); | ||
| 118 | |||
| 119 | /** | ||
| 120 | * \brief Draws a segment in the target image. | ||
| 121 | * \details No clipping is done. It is the responsibility of the | ||
| 122 | * caller to give valid coordinates. | ||
| 123 | * \param[in] p1 , p2 the two extremities of the segment. | ||
| 124 | * coordinates are in the [0,1]x[0,1] square. | ||
| 125 | * \param[in] c the color | ||
| 126 | */ | ||
| 127 | void segment(const vec2& p1, const vec2& p2, const Color& c) { | ||
| 128 | segment(transform(p1), transform(p2), c); | ||
| 129 | } | ||
| 130 | |||
| 131 | /** | ||
| 132 | * \brief Fills a circle in the target image | ||
| 133 | * \details The circle is clipped to the image | ||
| 134 | * \param[in] C the center of the circle, integer coordinates | ||
| 135 | * are in [0..width-1]x[0..height-1], where width | ||
| 136 | * and height are the sizes of the target image. | ||
| 137 | * \param[in] radius the radius of the circle (in pixels). | ||
| 138 | * \param[in] c the color of the pixels | ||
| 139 | */ | ||
| 140 | void fillcircle(const vec2i& C, int radius, const Color& c); | ||
| 141 | |||
| 142 | /** | ||
| 143 | * \brief Fills a circle in the target image | ||
| 144 | * \details The circle is clipped to the image | ||
| 145 | * \param[in] C the center of the circle, coordinates | ||
| 146 | * are between 0.0 and 1.0. | ||
| 147 | * \param[in] radius the radius of the circle. A value of | ||
| 148 | * 1.0 corresponds to the width of the image. | ||
| 149 | * \param[in] c the color of the pixels | ||
| 150 | */ | ||
| 151 | void fillcircle(const vec2& C, double radius, const Color& c) { | ||
| 152 | fillcircle( | ||
| 153 | transform(C), | ||
| 154 | int(radius*double(image_->width())), | ||
| 155 | c | ||
| 156 | ); | ||
| 157 | } | ||
| 158 | |||
| 159 | /** | ||
| 160 | * \brief Flood-fill from a given pixel | ||
| 161 | * \details Fills the connected component of black (zero) pixels | ||
| 162 | * incident to x,y | ||
| 163 | */ | ||
| 164 | void flood_fill(int x, int y, const Color& c); | ||
| 165 | |||
| 166 | /** | ||
| 167 | * \brief Sets a pixel of the image. | ||
| 168 | * \details Only BYTE, FLOAT32 and FLOAT64 component encoding | ||
| 169 | * are supported. If the image has less than 4 channels, the | ||
| 170 | * extra channels in \p c are ignored. | ||
| 171 | * \param[in] x , y the integer pixel coordinates | ||
| 172 | * \param[in] c the color of the pixel | ||
| 173 | */ | ||
| 174 | ✗ | void set_pixel(int x, int y, const Color& c) { | |
| 175 | ✗ | geo_debug_assert(x >= 0 && x < int(image_->width())); | |
| 176 | ✗ | geo_debug_assert(y >= 0 && y < int(image_->height())); | |
| 177 | ✗ | switch(component_encoding_) { | |
| 178 | ✗ | case Image::BYTE: { | |
| 179 | Memory::byte* pixel_ptr = | ||
| 180 | (Memory::byte*)( | ||
| 181 | ✗ | image_->pixel_base(index_t(x),index_t(y)) | |
| 182 | ); | ||
| 183 | ✗ | for(index_t comp=0; comp<nb_components_; ++comp) { | |
| 184 | ✗ | pixel_ptr[comp] = Memory::byte(c[comp] * 255.0); | |
| 185 | } | ||
| 186 | ✗ | } break; | |
| 187 | ✗ | case Image::FLOAT32: { | |
| 188 | Numeric::float32* pixel_ptr = | ||
| 189 | (Numeric::float32*)(void*)( | ||
| 190 | ✗ | image_->pixel_base(index_t(x),index_t(y)) | |
| 191 | ); | ||
| 192 | ✗ | for(index_t comp=0; comp<nb_components_; ++comp) { | |
| 193 | ✗ | pixel_ptr[comp] = Numeric::float32(c[comp]); | |
| 194 | } | ||
| 195 | ✗ | } break; | |
| 196 | ✗ | case Image::FLOAT64: { | |
| 197 | Numeric::float64* pixel_ptr = | ||
| 198 | (Numeric::float64*)(void*)( | ||
| 199 | ✗ | image_->pixel_base(index_t(x),index_t(y)) | |
| 200 | ); | ||
| 201 | ✗ | for(index_t comp=0; comp<nb_components_; ++comp) { | |
| 202 | ✗ | pixel_ptr[comp] = Numeric::float64(c[comp]); | |
| 203 | } | ||
| 204 | ✗ | } break; | |
| 205 | ✗ | case Image::INT16: | |
| 206 | case Image::INT32: { | ||
| 207 | ✗ | geo_assert_not_reached; | |
| 208 | } | ||
| 209 | } | ||
| 210 | ✗ | } | |
| 211 | |||
| 212 | /** | ||
| 213 | * \brief Tests whether a given pixel is black | ||
| 214 | * \details Only implemented for BYTE component encoding | ||
| 215 | * \param[in] x , y the integer coordinates of the pixel | ||
| 216 | * \retval true if the pixel is black | ||
| 217 | * \retval false otherwise | ||
| 218 | */ | ||
| 219 | ✗ | bool pixel_is_black(int x, int y) const { | |
| 220 | ✗ | Memory::byte* p = image_->pixel_base_byte_ptr( | |
| 221 | index_t(x),index_t(y) | ||
| 222 | ); | ||
| 223 | ✗ | bool result = true; | |
| 224 | ✗ | for(size_t c=0; c<image_->components_per_pixel(); ++c) { | |
| 225 | ✗ | result = result && (*p == 0); | |
| 226 | } | ||
| 227 | ✗ | return result; | |
| 228 | } | ||
| 229 | |||
| 230 | protected: | ||
| 231 | |||
| 232 | /** | ||
| 233 | * \brief Transforms a 2d point from world space to pixel coordinates. | ||
| 234 | * \param[in] p coordinates of the points, in [0,1]x[0,1] | ||
| 235 | * \return transformed the pixel coordinates of the point. | ||
| 236 | */ | ||
| 237 | ✗ | vec2i transform(const vec2& p) const { | |
| 238 | ✗ | return vec2i( | |
| 239 | ✗ | Numeric::int32(double(image_->width()-1)*p.x), | |
| 240 | ✗ | Numeric::int32(double(image_->height()-1)*p.y) | |
| 241 | ✗ | ); | |
| 242 | } | ||
| 243 | |||
| 244 | /** | ||
| 245 | * \brief Computes the linear interpolation between three colors. | ||
| 246 | * \param[in] c1 , c2 , c3 the three colors to be interpolated. | ||
| 247 | * \param[in] l1 , l2 , l3 the coefficients of the interpolation. | ||
| 248 | * \param[out] c the interpolated color. | ||
| 249 | */ | ||
| 250 | ✗ | void interpolate_color( | |
| 251 | const Color& c1, const Color& c2, const Color& c3, | ||
| 252 | double l1, double l2, double l3, | ||
| 253 | Color& c | ||
| 254 | ) const { | ||
| 255 | ✗ | c[0] = l1*c1[0] + l2*c2[0] + l3*c3[0]; | |
| 256 | ✗ | c[1] = l1*c1[1] + l2*c2[1] + l3*c3[1]; | |
| 257 | ✗ | c[2] = l1*c1[2] + l2*c2[2] + l3*c3[2]; | |
| 258 | ✗ | c[3] = l1*c1[3] + l2*c2[3] + l3*c3[3]; | |
| 259 | ✗ | } | |
| 260 | |||
| 261 | private: | ||
| 262 | Image* image_; | ||
| 263 | Image::ComponentEncoding component_encoding_; | ||
| 264 | index_t nb_components_; | ||
| 265 | }; | ||
| 266 | } | ||
| 267 | |||
| 268 | #endif | ||
| 269 |