OpenClonk
StdPNG.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
5  * Copyright (c) 2011-2016, The OpenClonk Team and contributors
6  *
7  * Distributed under the terms of the ISC license; see accompanying file
8  * "COPYING" for details.
9  *
10  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11  * See accompanying file "TRADEMARK" for details.
12  *
13  * To redistribute this file separately, substitute the full license texts
14  * for the above references.
15  */
16 // png file reading functionality
17 
18 #include "C4Include.h"
19 #include "graphics/StdPNG.h"
20 
21 #include "lib/StdColors.h"
22 #include "platform/StdScheduler.h"
23 
24 // png reading proc
25 void PNGAPI CPNGFile::CPNGReadFn(png_structp png_ptr, png_bytep data, size_t length)
26 {
27  static_cast<CPNGFile*>(png_get_io_ptr(png_ptr))->Read(data, length);
28 }
29 
30 void CPNGFile::Read(unsigned char *pData, int iLength)
31 {
32  // fixme: overflow check schould be done here
33  // simply copy into buffer
34  memcpy(pData, pFilePtr, iLength);
35  // advance file ptr
36  pFilePtr+=iLength;
37 }
38 
39 bool CPNGFile::DoLoad()
40 {
41  // reset file ptr
42  pFilePtr=pFile;
43  // check file
44  if (png_sig_cmp((unsigned char *) pFilePtr, 0, 8)) return false;
45  // setup png for reading
46  fWriteMode=false;
47  png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
48  if (!png_ptr) return false;
49  info_ptr = png_create_info_struct(png_ptr);
50  if (!info_ptr) return false;
51  end_info = png_create_info_struct(png_ptr);
52  if (!end_info) return false;
53  // error handling
54  if (setjmp(png_jmpbuf(png_ptr))) return false;
55  // set file-reading proc
56  png_set_read_fn(png_ptr, this, &CPNGFile::CPNGReadFn);
57  // read info
58  png_read_info(png_ptr, info_ptr);
59  // assign local vars
60  png_uint_32 uWdt = iWdt, uHgt = iHgt;
61  png_get_IHDR(png_ptr, info_ptr, &uWdt, &uHgt, &iBPC, &iClrType, &iIntrlcType, &iCmprType, &iFltrType);
62  // convert to bgra
63  if (iClrType == PNG_COLOR_TYPE_PALETTE && iBPC <= 8) png_set_expand(png_ptr);
64  if (iClrType == PNG_COLOR_TYPE_GRAY && iBPC < 8) png_set_expand(png_ptr);
65  if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_expand(png_ptr);
66  if (iBPC == 16) png_set_strip_16(png_ptr);
67  if (iBPC < 8) png_set_packing(png_ptr);
68  if (iClrType == PNG_COLOR_TYPE_GRAY || iClrType == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr);
69  png_set_bgr(png_ptr);
70  // update info
71  png_read_update_info(png_ptr, info_ptr);
72  png_get_IHDR(png_ptr, info_ptr, &uWdt, &uHgt, &iBPC, &iClrType, &iIntrlcType, &iCmprType, &iFltrType);
73  iWdt = uWdt; iHgt = uHgt;
74  // get bit depth/check format
75  switch (iClrType)
76  {
77  case PNG_COLOR_TYPE_RGB: iPixSize=3; break;
78  case PNG_COLOR_TYPE_RGB_ALPHA: iPixSize=4; break;
79  default: return false; // unrecognized image
80  }
81  // allocate mem for the whole image
82  iRowSize=png_get_rowbytes(png_ptr, info_ptr);
83  pImageData = new unsigned char[iRowSize*iHgt];
84  // create row ptr buffer
85  unsigned char **ppRowBuf = new unsigned char *[iHgt];
86  unsigned char **ppRows=ppRowBuf; unsigned char *pRow=pImageData;
87  for (unsigned int i=0; i<iHgt; ++i,pRow+=iRowSize) *ppRows++=pRow;
88  // read image!
89  png_read_image(png_ptr, ppRowBuf);
90  // free row buffer
91  delete [] ppRowBuf;
92  // success
93  return true;
94 }
95 
97 {
98  Default();
99 }
100 
102 {
103  // zero fields
104  pFile=nullptr;
105  fpFileOwned=false;
106  pFilePtr=nullptr;
107  png_ptr=nullptr;
108  info_ptr=end_info=nullptr;
109  pImageData=nullptr;
110  iRowSize=0;
111  iPixSize=0;
112  fp=nullptr;
113 }
114 
116 {
117  // clear
118  Clear();
119 }
120 
122 {
123  // clear internal png ptrs
124  if (png_ptr || info_ptr || end_info)
125  {
126  if (fWriteMode)
127  png_destroy_write_struct(&png_ptr, &info_ptr);
128  else
129  png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
130  }
131  png_ptr=nullptr;
132  info_ptr=end_info=nullptr;
133  fWriteMode=false;
134 }
135 
137 {
138  // free image data
139  if (pImageData) { delete [] pImageData; pImageData=nullptr; }
140  // clear internal png ptrs
141  ClearPngStructs();
142  // free file ptr if owned
143  if (fpFileOwned)
144  delete [] pFile;
145  pFile=nullptr;
146  // reset fields
147  fpFileOwned=false;
148  pFilePtr=nullptr;
149  iRowSize=0;
150  iPixSize=0;
151  // close file if open
152  if (fp) { fclose(fp); fp=nullptr; }
153 }
154 
155 bool CPNGFile::Load(unsigned char *pFile, int iSize)
156 {
157  // clear any previously loaded file
158  Clear();
159  // store file ptr as not owned
160  this->pFile = pFile;
161  iFileSize=iSize;
162  fpFileOwned=false;
163  // perform the loading
164  if (!DoLoad())
165  {
166  Clear();
167  return false;
168  }
169  // reset file-field
170  this->pFile = nullptr; iFileSize=0;
171  // success
172  return true;
173 }
174 
175 DWORD CPNGFile::GetPix(int iX, int iY)
176 {
177  // image loaded?
178  if (!pImageData) return 0;
179  // return pixel value
180  unsigned char *pPix=pImageData+iY*iRowSize+iX*iPixSize;
181  switch (iClrType)
182  {
183  case PNG_COLOR_TYPE_RGB:
184  return C4RGB(pPix[2], pPix[1], pPix[0]);
185  case PNG_COLOR_TYPE_RGB_ALPHA:
186  return RGBA(pPix[2], pPix[1], pPix[0], pPix[3]);
187  }
188  return 0;
189 }
190 
191 bool CPNGFile::Create(int iWdt, int iHgt, bool fAlpha)
192 {
193  // catch invalid size
194  if (iWdt<=0 || iHgt<=0) return false;
195  // clear current image in mem
196  Clear();
197  // set dimensions
198  this->iWdt=iWdt; this->iHgt=iHgt;
199  // set color type
200  iBPC = 8;
201  iClrType = fAlpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB;
202  iPixSize = fAlpha ? 4 : 3;
203  // set interlacing, filters and stuff
204  iIntrlcType = PNG_INTERLACE_NONE;
205  iCmprType = PNG_COMPRESSION_TYPE_DEFAULT;
206  iFltrType = PNG_FILTER_TYPE_DEFAULT;
207  // calculate rowbytes
208  int iBPP = (fAlpha ? 4 : 3) * iBPC;
209  iRowSize = (iBPP*iWdt+7)>>3;
210  // create image data
211  pImageData = new unsigned char[iHgt * iRowSize];
212  // success
213  return true;
214 }
215 
216 bool CPNGFile::SetPix(int iX, int iY, DWORD dwValue)
217 {
218  // image created?
219  if (!pImageData) return false;
220  // set pixel value
221  unsigned char *pPix=pImageData+iY*iRowSize+iX*iPixSize;
222  switch (iClrType)
223  {
224  case PNG_COLOR_TYPE_RGB: // RGB: set r, g and b values
225  pPix[0] = GetBlueValue(dwValue);
226  pPix[1] = GetGreenValue(dwValue);
227  pPix[2] = GetRedValue(dwValue);
228  return true;
229  case PNG_COLOR_TYPE_RGB_ALPHA: // RGBA: simply set in mem
230  *(DWORD *) pPix = dwValue;
231  return true;
232  }
233  return false;
234 }
235 
236 bool CPNGFile::Save(const char *szFilename)
237 {
238  // regular file saving - first, there has to be a buffer
239  if (!pImageData) return false;
240  // open the file
241  fp = fopen(szFilename, "wb"); if (!fp) return false;
242  // clear any previously initialized png-structs (e.g. by reading)
243  ClearPngStructs();
244  // reinit them for writing
245  fWriteMode=true;
246  png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
247  if (!png_ptr) { Clear(); return false; }
248  info_ptr = png_create_info_struct(png_ptr);
249  if (!info_ptr) { Clear(); return false; }
250  // error handling
251  if (setjmp(png_jmpbuf(png_ptr))) { Clear(); return false; }
252  // io initialization
253  png_init_io(png_ptr, fp);
254  // compression stuff
255  png_set_filter(png_ptr, 0, PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_PAETH);
256  png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
257  png_set_compression_mem_level(png_ptr, 8);
258  png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY);
259  png_set_compression_window_bits(png_ptr, 15);
260  png_set_compression_method(png_ptr, 8);
261  // set header
262  png_set_IHDR(png_ptr, info_ptr, iWdt, iHgt, iBPC, iClrType, iIntrlcType, iCmprType, iFltrType);
263  // double-check our calculated row size
264  int iRealRowSize=png_get_rowbytes(png_ptr, info_ptr);
265  if (iRealRowSize != iRowSize)
266  {
267  // this won't go well, so better abort
268  Clear(); return false;
269  }
270  // write png header
271  png_write_info(png_ptr, info_ptr);
272  // image data is given as bgr...
273  png_set_bgr(png_ptr);
274  // create row array
275  unsigned char **ppRowBuf = new unsigned char *[iHgt];
276  unsigned char **ppRows=ppRowBuf; unsigned char *pRow=pImageData;
277  for (unsigned int i=0; i<iHgt; ++i,pRow+=iRowSize) *ppRows++=pRow;
278  // write image
279  png_write_image(png_ptr, ppRowBuf);
280  // free row buffer
281  delete [] ppRowBuf;
282  // write end struct
283  png_write_end(png_ptr, info_ptr);
284  // finally, close the file
285  fclose(fp); fp = nullptr;
286  // clear png structs
287  ClearPngStructs();
288  // success!
289  return true;
290 }
291 
293 {
294  switch (iClrType)
295  {
296  case PNG_COLOR_TYPE_RGB: return 24;
297  case PNG_COLOR_TYPE_RGB_ALPHA: return 32;
298  }
299  return 0;
300 }
301 
302 /* Background-threaded screenshot saving */
303 
304 class CPNGSaveThread : public StdThread
305 {
306 private:
307  std::unique_ptr<CPNGFile> png;
308  StdCopyStrBuf filename;
309 
310  static CStdCSec threads_sec;
311  static std::list<CPNGSaveThread *> threads;
312 public:
313  CPNGSaveThread(CPNGFile *png, const char *filename);
314  ~CPNGSaveThread() override;
315 
316  static bool HasPendingThreads();
317 
318 protected:
319  void Execute() override;
320  bool IsSelfDestruct() override { return true; }
321 };
322 
323 CStdCSec CPNGSaveThread::threads_sec;
324 std::list<CPNGSaveThread *> CPNGSaveThread::threads;
325 
326 CPNGSaveThread::CPNGSaveThread(CPNGFile *png, const char *filename) : png(png), filename(filename)
327 {
328  // keep track of current saves
329  CStdLock lock(&threads_sec);
330  threads.push_back(this);
331 }
332 
334 {
335  // keep track of current saves
336  CStdLock lock(&threads_sec);
337  threads.remove(this);
338 }
339 
341 {
342  // Save without feedback. There's no way to post e.g. a log message to the main thread at the moment.
343  // But if saving fails, there's just a missing screenshot, which shouldn't be a big deal.
344  png->Save(filename.getData());
345  SignalStop();
346 }
347 
349 {
350  CStdLock lock(&threads_sec);
351  return !threads.empty();
352 }
353 
354 void CPNGFile::ScheduleSaving(CPNGFile *png, const char *filename)
355 {
356  // start a background thread to save the png file
357  // thread is responsible for cleaning up
358  CPNGSaveThread *saver = new CPNGSaveThread(png, filename);
359  if (!saver->Start()) delete saver;
360 }
361 
363 {
364  // Yield main thread until all pending saves have finished.Wait for
365  bool first = true;
367  {
368  // English message because localization data is no longer loaded
369  if (first) LogSilent("Waiting for pending image files to be written to disc...");
370  first = false;
371 #ifdef HAVE_WINTHREAD
372  Sleep(100);
373 #else
374  sched_yield();
375 #endif
376  }
377 }
bool LogSilent(const char *szMessage, bool fConsole)
Definition: C4Log.cpp:126
uint32_t DWORD
uint32_t RGBA(uint32_t r, uint32_t g, uint32_t b, uint32_t a)
Definition: StdColors.h:22
#define C4RGB(r, g, b)
Definition: StdColors.h:26
#define GetRedValue(rgb)
Definition: StdColors.h:29
#define GetGreenValue(rgb)
Definition: StdColors.h:28
#define GetBlueValue(rgb)
Definition: StdColors.h:27
int iSize
Definition: TstC4NetIO.cpp:32
void Clear()
Definition: StdPNG.cpp:136
bool Create(int iWdt, int iHgt, bool fAlpha)
Definition: StdPNG.cpp:191
void ClearPngStructs()
Definition: StdPNG.cpp:121
~CPNGFile()
Definition: StdPNG.cpp:115
int iCmprType
Definition: StdPNG.h:45
int iIntrlcType
Definition: StdPNG.h:45
unsigned long iHgt
Definition: StdPNG.h:44
int iBPC
Definition: StdPNG.h:45
int iClrType
Definition: StdPNG.h:45
int iFltrType
Definition: StdPNG.h:45
CPNGFile()
Definition: StdPNG.cpp:96
bool SetPix(int iX, int iY, DWORD dwValue)
Definition: StdPNG.cpp:216
bool Save(const char *szFilename)
Definition: StdPNG.cpp:236
static void ScheduleSaving(CPNGFile *png, const char *filename)
Definition: StdPNG.cpp:354
bool Load(BYTE *pFile, int iSize)
Definition: StdPNG.cpp:155
unsigned long iWdt
Definition: StdPNG.h:44
void Default()
Definition: StdPNG.cpp:101
DWORD GetPix(int iX, int iY)
Definition: StdPNG.cpp:175
static void WaitForSaves()
Definition: StdPNG.cpp:362
int GetBitsPerPixel()
Definition: StdPNG.cpp:292
static bool HasPendingThreads()
Definition: StdPNG.cpp:348
~CPNGSaveThread() override
Definition: StdPNG.cpp:333
CPNGSaveThread(CPNGFile *png, const char *filename)
Definition: StdPNG.cpp:326
void Execute() override
Definition: StdPNG.cpp:340
bool IsSelfDestruct() override
Definition: StdPNG.cpp:320
const char * getData() const
Definition: StdBuf.h:442
void SignalStop()
bool Start()