Visual Servoing Platform  version 3.6.1 under development (2025-01-13)
vpImageIoStb.cpp
1 /*
2  * ViSP, open source Visual Servoing Platform software.
3  * Copyright (C) 2005 - 2024 by Inria. All rights reserved.
4  *
5  * This software is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  * See the file LICENSE.txt at the root directory of this source
10  * distribution for additional information about the GNU GPL.
11  *
12  * For using ViSP with software that can not be combined with the GNU
13  * GPL, please contact Inria about acquiring a ViSP Professional
14  * Edition License.
15  *
16  * See https://visp.inria.fr for more information.
17  *
18  * This software was developed at:
19  * Inria Rennes - Bretagne Atlantique
20  * Campus Universitaire de Beaulieu
21  * 35042 Rennes Cedex
22  * France
23  *
24  * If you have questions regarding the use of this file, please contact
25  * Inria at visp@inria.fr
26  *
27  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29  *
30  * Description:
31  * stb backend for JPEG and PNG image I/O operations.
32  */
33 
39 #include <visp3/core/vpConfig.h>
40 
41 #if defined(VISP_HAVE_STBIMAGE)
42 
43 #include "vpImageIoBackend.h"
44 #include <visp3/core/vpImageConvert.h>
45 
46 #if defined __SSE2__ || defined _M_X64 || (defined _M_IX86_FP && _M_IX86_FP >= 2)
47 #define VISP_HAVE_SSE2 1
48 #endif
49 
50 #ifndef VISP_HAVE_SSE2
51 #define STBI_NO_SIMD
52 #endif
53 
54 #define STB_IMAGE_IMPLEMENTATION
55 #include <stb_image.h>
56 
57 #define STB_IMAGE_WRITE_IMPLEMENTATION
58 #include <stb_image_write.h>
59 
60 BEGIN_VISP_NAMESPACE
61 
62 void readStb(vpImage<unsigned char> &I, const std::string &filename)
63 {
64  int width = 0, height = 0, channels = 0;
65  unsigned char *image = stbi_load(filename.c_str(), &width, &height, &channels, STBI_grey);
66  if (image == nullptr) {
67  throw(vpImageException(vpImageException::ioError, "Can't read the image: %s", filename.c_str()));
68  }
69  I.init(image, static_cast<unsigned int>(height), static_cast<unsigned int>(width), true);
70  stbi_image_free(image);
71 }
72 
73 void readStb(vpImage<vpRGBa> &I, const std::string &filename)
74 {
75  int width = 0, height = 0, channels = 0;
76  unsigned char *image = stbi_load(filename.c_str(), &width, &height, &channels, STBI_rgb_alpha);
77  if (image == nullptr) {
78  throw(vpImageException(vpImageException::ioError, "Can't read the image: %s", filename.c_str()));
79  }
80  I.init(reinterpret_cast<vpRGBa *>(image), static_cast<unsigned int>(height), static_cast<unsigned int>(width), true);
81  stbi_image_free(image);
82 }
83 
84 void writeJPEGStb(const vpImage<unsigned char> &I, const std::string &filename, int quality)
85 {
86  int res = stbi_write_jpg(filename.c_str(), static_cast<int>(I.getWidth()), static_cast<int>(I.getHeight()), STBI_grey,
87  reinterpret_cast<void *>(I.bitmap), quality);
88  if (res == 0) {
89  throw(vpImageException(vpImageException::ioError, "JEPG write error"));
90  }
91 }
92 
93 void writeJPEGStb(const vpImage<vpRGBa> &I, const std::string &filename, int quality)
94 {
95  int res = stbi_write_jpg(filename.c_str(), static_cast<int>(I.getWidth()), static_cast<int>(I.getHeight()),
96  STBI_rgb_alpha, reinterpret_cast<void *>(I.bitmap), quality);
97  if (res == 0) {
98  throw(vpImageException(vpImageException::ioError, "JEPG write error"));
99  }
100 }
101 
102 void writePNGStb(const vpImage<unsigned char> &I, const std::string &filename)
103 {
104  const int stride_in_bytes = static_cast<int>(I.getWidth());
105  int res = stbi_write_png(filename.c_str(), static_cast<int>(I.getWidth()), static_cast<int>(I.getHeight()), STBI_grey,
106  reinterpret_cast<void *>(I.bitmap), stride_in_bytes);
107  if (res == 0) {
108  throw(vpImageException(vpImageException::ioError, "PNG write error: %s", filename.c_str()));
109  }
110 }
111 
112 void writePNGStb(const vpImage<vpRGBa> &I, const std::string &filename)
113 {
114  const int stride_in_bytes = static_cast<int>(4 * I.getWidth());
115  int res = stbi_write_png(filename.c_str(), static_cast<int>(I.getWidth()), static_cast<int>(I.getHeight()),
116  STBI_rgb_alpha, reinterpret_cast<void *>(I.bitmap), stride_in_bytes);
117  if (res == 0) {
118  throw(vpImageException(vpImageException::ioError, "PNG write error: %s", filename.c_str()));
119  }
120 }
121 
122 namespace
123 {
124 typedef struct
125 {
126  int last_pos;
127  void *context;
128 } custom_stbi_mem_context;
129 
130 // custom write function
131 static void custom_stbi_write_mem(void *context, void *data, int size)
132 {
133  custom_stbi_mem_context *c = (custom_stbi_mem_context *)context;
134  char *dst = (char *)c->context;
135  char *src = (char *)data;
136  int cur_pos = c->last_pos;
137  for (int i = 0; i < size; ++i) {
138  dst[cur_pos++] = src[i];
139  }
140  c->last_pos = cur_pos;
141 }
142 }
143 
150 void readPNGfromMemStb(const std::vector<unsigned char> &buffer, vpImage<unsigned char> &I)
151 {
152  int x = 0, y = 0, comp = 0;
153  const int req_channels = 1;
154  unsigned char *buffer_read = stbi_load_from_memory(buffer.data(), static_cast<int>(buffer.size()), &x, &y, &comp, req_channels);
155  assert(comp == req_channels);
156 
157  I.init(buffer_read, y, x, true);
158  STBI_FREE(buffer_read);
159 }
160 
167 void readPNGfromMemStb(const std::vector<unsigned char> &buffer, vpImage<vpRGBa> &I_color)
168 {
169  int x = 0, y = 0, comp = 0;
170  unsigned char *buffer_read = stbi_load_from_memory(buffer.data(), static_cast<int>(buffer.size()), &x, &y, &comp, 0);
171 
172  if (comp == 4) {
173  const bool copyData = true;
174  I_color.init(reinterpret_cast<vpRGBa *>(buffer_read), y, x, copyData);
175  }
176  else if (comp == 3) {
177  I_color.init(y, x);
178  const bool flip = false;
179  vpImageConvert::RGBToRGBa(buffer_read, reinterpret_cast<unsigned char *>(I_color.bitmap), x, y, flip);
180  }
181  else {
182  STBI_FREE(buffer_read);
183 
184 #if VISP_CXX_STANDARD > VISP_CXX_STANDARD_98
185  std::string message = "Wrong number of channels for the input buffer: " + std::to_string(comp);
187 #else
188  throw(vpImageException(vpImageException::ioError, "Wrong number of channels for the input buffer: %d", comp));
189 #endif
190  }
191 
192  STBI_FREE(buffer_read);
193 }
194 
201 void writePNGtoMemStb(const vpImage<unsigned char> &I, std::vector<unsigned char> &buffer)
202 {
203  const int height = I.getRows();
204  const int width = I.getCols();
205  const int channels = 1;
206 
207  custom_stbi_mem_context context;
208  context.last_pos = 0;
209  buffer.resize(I.getHeight() * I.getWidth());
210  context.context = (void *)buffer.data();
211 
212  const int stride_bytes = 0;
213  int result = stbi_write_png_to_func(custom_stbi_write_mem, &context, width, height, channels, I.bitmap, stride_bytes);
214 
215  if (result) {
216  buffer.resize(context.last_pos);
217  }
218  else {
219 #if VISP_CXX_STANDARD > VISP_CXX_STANDARD_98
220  std::string message = "Cannot write png to memory, result: " + std::to_string(result);
222 #else
223  throw(vpImageException(vpImageException::ioError, "Cannot write png to memory, result: %d", result));
224 #endif
225  }
226 }
227 
235 void writePNGtoMemStb(const vpImage<vpRGBa> &I_color, std::vector<unsigned char> &buffer, bool saveAlpha)
236 {
237  const int height = I_color.getRows();
238  const int width = I_color.getCols();
239  const int channels = saveAlpha ? 4 : 3;
240 
241  custom_stbi_mem_context context;
242  context.last_pos = 0;
243  buffer.resize(height * width * channels);
244  context.context = (void *)buffer.data();
245 
246  const int stride_bytes = 0;
247  int result = 0;
248  if (saveAlpha) {
249  result = stbi_write_png_to_func(custom_stbi_write_mem, &context, width, height, channels,
250  reinterpret_cast<unsigned char *>(I_color.bitmap), stride_bytes);
251  }
252  else {
253  unsigned char *bitmap = new unsigned char[height * width * channels];
254  vpImageConvert::RGBaToRGB(reinterpret_cast<unsigned char *>(I_color.bitmap), bitmap, height*width);
255  result = stbi_write_png_to_func(custom_stbi_write_mem, &context, width, height, channels, bitmap, stride_bytes);
256  delete[] bitmap;
257  }
258 
259  if (result) {
260  buffer.resize(context.last_pos);
261  }
262  else {
263 #if VISP_CXX_STANDARD > VISP_CXX_STANDARD_98
264  std::string message = "Cannot write png to memory, result: " + std::to_string(result);
266 #else
267  throw(vpImageException(vpImageException::ioError, "Cannot write png to memory, result: %d", result));
268 #endif
269  }
270 }
271 
272 END_VISP_NAMESPACE
273 
274 #endif
static void RGBToRGBa(unsigned char *rgb, unsigned char *rgba, unsigned int size)
static void RGBaToRGB(unsigned char *rgba, unsigned char *rgb, unsigned int size)
Error that can be emitted by the vpImage class and its derivatives.
@ ioError
Image io error.
void init(unsigned int height, unsigned int width)
Set the size of the image.
Definition: vpImage.h:387
unsigned int getWidth() const
Definition: vpImage.h:242
unsigned int getCols() const
Definition: vpImage.h:171
Type * bitmap
points toward the bitmap
Definition: vpImage.h:135
unsigned int getHeight() const
Definition: vpImage.h:181
unsigned int getRows() const
Definition: vpImage.h:212
Definition: vpRGBa.h:70