Visual Servoing Platform  version 3.6.1 under development (2024-05-18)
vpImageIoStb.cpp
1 /*
2  * ViSP, open source Visual Servoing Platform software.
3  * Copyright (C) 2005 - 2023 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 void readStb(vpImage<unsigned char> &I, const std::string &filename)
61 {
62  int width = 0, height = 0, channels = 0;
63  unsigned char *image = stbi_load(filename.c_str(), &width, &height, &channels, STBI_grey);
64  if (image == nullptr) {
65  throw(vpImageException(vpImageException::ioError, "Can't read the image: %s", filename.c_str()));
66  }
67  I.init(image, static_cast<unsigned int>(height), static_cast<unsigned int>(width), true);
68  stbi_image_free(image);
69 }
70 
71 void readStb(vpImage<vpRGBa> &I, const std::string &filename)
72 {
73  int width = 0, height = 0, channels = 0;
74  unsigned char *image = stbi_load(filename.c_str(), &width, &height, &channels, STBI_rgb_alpha);
75  if (image == nullptr) {
76  throw(vpImageException(vpImageException::ioError, "Can't read the image: %s", filename.c_str()));
77  }
78  I.init(reinterpret_cast<vpRGBa *>(image), static_cast<unsigned int>(height), static_cast<unsigned int>(width), true);
79  stbi_image_free(image);
80 }
81 
82 void writeJPEGStb(const vpImage<unsigned char> &I, const std::string &filename, int quality)
83 {
84  int res = stbi_write_jpg(filename.c_str(), static_cast<int>(I.getWidth()), static_cast<int>(I.getHeight()), STBI_grey,
85  reinterpret_cast<void *>(I.bitmap), quality);
86  if (res == 0) {
87  throw(vpImageException(vpImageException::ioError, "JEPG write error"));
88  }
89 }
90 
91 void writeJPEGStb(const vpImage<vpRGBa> &I, const std::string &filename, int quality)
92 {
93  int res = stbi_write_jpg(filename.c_str(), static_cast<int>(I.getWidth()), static_cast<int>(I.getHeight()),
94  STBI_rgb_alpha, reinterpret_cast<void *>(I.bitmap), quality);
95  if (res == 0) {
96  throw(vpImageException(vpImageException::ioError, "JEPG write error"));
97  }
98 }
99 
100 void writePNGStb(const vpImage<unsigned char> &I, const std::string &filename)
101 {
102  const int stride_in_bytes = static_cast<int>(I.getWidth());
103  int res = stbi_write_png(filename.c_str(), static_cast<int>(I.getWidth()), static_cast<int>(I.getHeight()), STBI_grey,
104  reinterpret_cast<void *>(I.bitmap), stride_in_bytes);
105  if (res == 0) {
106  throw(vpImageException(vpImageException::ioError, "PNG write error: %s", filename.c_str()));
107  }
108 }
109 
110 void writePNGStb(const vpImage<vpRGBa> &I, const std::string &filename)
111 {
112  const int stride_in_bytes = static_cast<int>(4 * I.getWidth());
113  int res = stbi_write_png(filename.c_str(), static_cast<int>(I.getWidth()), static_cast<int>(I.getHeight()),
114  STBI_rgb_alpha, reinterpret_cast<void *>(I.bitmap), stride_in_bytes);
115  if (res == 0) {
116  throw(vpImageException(vpImageException::ioError, "PNG write error: %s", filename.c_str()));
117  }
118 }
119 
120 namespace
121 {
122 typedef struct
123 {
124  int last_pos;
125  void *context;
126 } custom_stbi_mem_context;
127 
128 // custom write function
129 static void custom_stbi_write_mem(void *context, void *data, int size)
130 {
131  custom_stbi_mem_context *c = (custom_stbi_mem_context *)context;
132  char *dst = (char *)c->context;
133  char *src = (char *)data;
134  int cur_pos = c->last_pos;
135  for (int i = 0; i < size; ++i) {
136  dst[cur_pos++] = src[i];
137  }
138  c->last_pos = cur_pos;
139 }
140 }
141 
148 void readPNGfromMemStb(const std::vector<unsigned char> &buffer, vpImage<unsigned char> &I)
149 {
150  int x = 0, y = 0, comp = 0;
151  const int req_channels = 1;
152  unsigned char *buffer_read = stbi_load_from_memory(buffer.data(), static_cast<int>(buffer.size()), &x, &y, &comp, req_channels);
153  assert(comp == req_channels);
154 
155  I.init(buffer_read, y, x, true);
156  STBI_FREE(buffer_read);
157 }
158 
165 void readPNGfromMemStb(const std::vector<unsigned char> &buffer, vpImage<vpRGBa> &I_color)
166 {
167  int x = 0, y = 0, comp = 0;
168  unsigned char *buffer_read = stbi_load_from_memory(buffer.data(), static_cast<int>(buffer.size()), &x, &y, &comp, 0);
169 
170  if (comp == 4) {
171  const bool copyData = true;
172  I_color.init(reinterpret_cast<vpRGBa *>(buffer_read), y, x, copyData);
173  }
174  else if (comp == 3) {
175  I_color.init(y, x);
176  const bool flip = false;
177  vpImageConvert::RGBToRGBa(buffer_read, reinterpret_cast<unsigned char *>(I_color.bitmap), x, y, flip);
178  }
179  else {
180  STBI_FREE(buffer_read);
181 
182 #if VISP_CXX_STANDARD > VISP_CXX_STANDARD_98
183  std::string message = "Wrong number of channels for the input buffer: " + std::to_string(comp);
185 #else
186  throw(vpImageException(vpImageException::ioError, "Wrong number of channels for the input buffer: %d", comp));
187 #endif
188  }
189 
190  STBI_FREE(buffer_read);
191 }
192 
199 void writePNGtoMemStb(const vpImage<unsigned char> &I, std::vector<unsigned char> &buffer)
200 {
201  const int height = I.getRows();
202  const int width = I.getCols();
203  const int channels = 1;
204 
205  custom_stbi_mem_context context;
206  context.last_pos = 0;
207  buffer.resize(I.getHeight() * I.getWidth());
208  context.context = (void *)buffer.data();
209 
210  const int stride_bytes = 0;
211  int result = stbi_write_png_to_func(custom_stbi_write_mem, &context, width, height, channels, I.bitmap, stride_bytes);
212 
213  if (result) {
214  buffer.resize(context.last_pos);
215  }
216  else {
217 #if VISP_CXX_STANDARD > VISP_CXX_STANDARD_98
218  std::string message = "Cannot write png to memory, result: " + std::to_string(result);
220 #else
221  throw(vpImageException(vpImageException::ioError, "Cannot write png to memory, result: %d", result));
222 #endif
223  }
224 }
225 
233 void writePNGtoMemStb(const vpImage<vpRGBa> &I_color, std::vector<unsigned char> &buffer, bool saveAlpha)
234 {
235  const int height = I_color.getRows();
236  const int width = I_color.getCols();
237  const int channels = saveAlpha ? 4 : 3;
238 
239  custom_stbi_mem_context context;
240  context.last_pos = 0;
241  buffer.resize(height * width * channels);
242  context.context = (void *)buffer.data();
243 
244  const int stride_bytes = 0;
245  int result = 0;
246  if (saveAlpha) {
247  result = stbi_write_png_to_func(custom_stbi_write_mem, &context, width, height, channels,
248  reinterpret_cast<unsigned char *>(I_color.bitmap), stride_bytes);
249  }
250  else {
251  unsigned char *bitmap = new unsigned char[height * width * channels];
252  vpImageConvert::RGBaToRGB(reinterpret_cast<unsigned char *>(I_color.bitmap), bitmap, height*width);
253  result = stbi_write_png_to_func(custom_stbi_write_mem, &context, width, height, channels, bitmap, stride_bytes);
254  delete[] bitmap;
255  }
256 
257  if (result) {
258  buffer.resize(context.last_pos);
259  }
260  else {
261 #if VISP_CXX_STANDARD > VISP_CXX_STANDARD_98
262  std::string message = "Cannot write png to memory, result: " + std::to_string(result);
264 #else
265  throw(vpImageException(vpImageException::ioError, "Cannot write png to memory, result: %d", result));
266 #endif
267  }
268 }
269 #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:642
unsigned int getWidth() const
Definition: vpImage.h:245
unsigned int getCols() const
Definition: vpImage.h:175
Type * bitmap
points toward the bitmap
Definition: vpImage.h:139
unsigned int getHeight() const
Definition: vpImage.h:184
unsigned int getRows() const
Definition: vpImage.h:215
Definition: vpRGBa.h:61