OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
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 (pFile && fpFileOwned) delete [] pFile; pFile=nullptr;
144  // reset fields
145  fpFileOwned=false;
146  pFilePtr=nullptr;
147  iRowSize=0;
148  iPixSize=0;
149  // close file if open
150  if (fp) { fclose(fp); fp=nullptr; }
151 }
152 
153 bool CPNGFile::Load(unsigned char *pFile, int iSize)
154 {
155  // clear any previously loaded file
156  Clear();
157  // store file ptr as not owned
158  this->pFile = pFile;
159  iFileSize=iSize;
160  fpFileOwned=false;
161  // perform the loading
162  if (!DoLoad())
163  {
164  Clear();
165  return false;
166  }
167  // reset file-field
168  this->pFile = nullptr; iFileSize=0;
169  // success
170  return true;
171 }
172 
173 DWORD CPNGFile::GetPix(int iX, int iY)
174 {
175  // image loaded?
176  if (!pImageData) return 0;
177  // return pixel value
178  unsigned char *pPix=pImageData+iY*iRowSize+iX*iPixSize;
179  switch (iClrType)
180  {
181  case PNG_COLOR_TYPE_RGB:
182  return C4RGB(pPix[2], pPix[1], pPix[0]);
183  case PNG_COLOR_TYPE_RGB_ALPHA:
184  return RGBA(pPix[2], pPix[1], pPix[0], pPix[3]);
185  }
186  return 0;
187 }
188 
189 bool CPNGFile::Create(int iWdt, int iHgt, bool fAlpha)
190 {
191  // catch invalid size
192  if (iWdt<=0 || iHgt<=0) return false;
193  // clear current image in mem
194  Clear();
195  // set dimensions
196  this->iWdt=iWdt; this->iHgt=iHgt;
197  // set color type
198  iBPC = 8;
199  iClrType = fAlpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB;
200  iPixSize = fAlpha ? 4 : 3;
201  // set interlacing, filters and stuff
202  iIntrlcType = PNG_INTERLACE_NONE;
203  iCmprType = PNG_COMPRESSION_TYPE_DEFAULT;
204  iFltrType = PNG_FILTER_TYPE_DEFAULT;
205  // calculate rowbytes
206  int iBPP = (fAlpha ? 4 : 3) * iBPC;
207  iRowSize = (iBPP*iWdt+7)>>3;
208  // create image data
209  pImageData = new unsigned char[iHgt * iRowSize];
210  // success
211  return true;
212 }
213 
214 bool CPNGFile::SetPix(int iX, int iY, DWORD dwValue)
215 {
216  // image created?
217  if (!pImageData) return false;
218  // set pixel value
219  unsigned char *pPix=pImageData+iY*iRowSize+iX*iPixSize;
220  switch (iClrType)
221  {
222  case PNG_COLOR_TYPE_RGB: // RGB: set r, g and b values
223  pPix[0] = GetBlueValue(dwValue);
224  pPix[1] = GetGreenValue(dwValue);
225  pPix[2] = GetRedValue(dwValue);
226  return true;
227  case PNG_COLOR_TYPE_RGB_ALPHA: // RGBA: simply set in mem
228  *(DWORD *) pPix = dwValue;
229  return true;
230  }
231  return false;
232 }
233 
234 bool CPNGFile::Save(const char *szFilename)
235 {
236  // regular file saving - first, there has to be a buffer
237  if (!pImageData) return false;
238  // open the file
239  fp = fopen(szFilename, "wb"); if (!fp) return false;
240  // clear any previously initialized png-structs (e.g. by reading)
241  ClearPngStructs();
242  // reinit them for writing
243  fWriteMode=true;
244  png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
245  if (!png_ptr) { Clear(); return false; }
246  info_ptr = png_create_info_struct(png_ptr);
247  if (!info_ptr) { Clear(); return false; }
248  // error handling
249  if (setjmp(png_jmpbuf(png_ptr))) { Clear(); return false; }
250  // io initialization
251  png_init_io(png_ptr, fp);
252  // compression stuff
253  png_set_filter(png_ptr, 0, PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_PAETH);
254  png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
255  png_set_compression_mem_level(png_ptr, 8);
256  png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY);
257  png_set_compression_window_bits(png_ptr, 15);
258  png_set_compression_method(png_ptr, 8);
259  // set header
260  png_set_IHDR(png_ptr, info_ptr, iWdt, iHgt, iBPC, iClrType, iIntrlcType, iCmprType, iFltrType);
261  // double-check our calculated row size
262  int iRealRowSize=png_get_rowbytes(png_ptr, info_ptr);
263  if (iRealRowSize != iRowSize)
264  {
265  // this won't go well, so better abort
266  Clear(); return false;
267  }
268  // write png header
269  png_write_info(png_ptr, info_ptr);
270  // image data is given as bgr...
271  png_set_bgr(png_ptr);
272  // create row array
273  unsigned char **ppRowBuf = new unsigned char *[iHgt];
274  unsigned char **ppRows=ppRowBuf; unsigned char *pRow=pImageData;
275  for (unsigned int i=0; i<iHgt; ++i,pRow+=iRowSize) *ppRows++=pRow;
276  // write image
277  png_write_image(png_ptr, ppRowBuf);
278  // free row buffer
279  delete [] ppRowBuf;
280  // write end struct
281  png_write_end(png_ptr, info_ptr);
282  // finally, close the file
283  fclose(fp); fp = nullptr;
284  // clear png structs
285  ClearPngStructs();
286  // success!
287  return true;
288 }
289 
291 {
292  switch (iClrType)
293  {
294  case PNG_COLOR_TYPE_RGB: return 24;
295  case PNG_COLOR_TYPE_RGB_ALPHA: return 32;
296  }
297  return 0;
298 }
299 
300 /* Background-threaded screenshot saving */
301 
302 class CPNGSaveThread : public StdThread
303 {
304 private:
305  std::unique_ptr<CPNGFile> png;
306  StdCopyStrBuf filename;
307 
308  static CStdCSec threads_sec;
309  static std::list<CPNGSaveThread *> threads;
310 public:
311  CPNGSaveThread(CPNGFile *png, const char *filename);
312  virtual ~CPNGSaveThread();
313 
314  static bool HasPendingThreads();
315 
316 protected:
317  virtual void Execute();
318  virtual bool IsSelfDestruct() { return true; }
319 };
320 
321 CStdCSec CPNGSaveThread::threads_sec;
322 std::list<CPNGSaveThread *> CPNGSaveThread::threads;
323 
324 CPNGSaveThread::CPNGSaveThread(CPNGFile *png, const char *filename) : png(png), filename(filename)
325 {
326  // keep track of current saves
327  CStdLock lock(&threads_sec);
328  threads.push_back(this);
329 }
330 
332 {
333  // keep track of current saves
334  CStdLock lock(&threads_sec);
335  threads.remove(this);
336 }
337 
339 {
340  // Save without feedback. There's no way to post e.g. a log message to the main thread at the moment.
341  // But if saving fails, there's just a missing screenshot, which shouldn't be a big deal.
342  png->Save(filename.getData());
343  SignalStop();
344 }
345 
347 {
348  CStdLock lock(&threads_sec);
349  return !threads.empty();
350 }
351 
352 void CPNGFile::ScheduleSaving(CPNGFile *png, const char *filename)
353 {
354  // start a background thread to save the png file
355  // thread is responsible for cleaning up
356  CPNGSaveThread *saver = new CPNGSaveThread(png, filename);
357  if (!saver->Start()) delete saver;
358 }
359 
361 {
362  // Yield main thread until all pending saves have finished.Wait for
363  bool first = true;
365  {
366  // English message because localization data is no longer loaded
367  if (first) LogSilent("Waiting for pending image files to be written to disc...");
368  first = false;
369 #ifdef HAVE_WINTHREAD
370  Sleep(100);
371 #else
372  sched_yield();
373 #endif
374  }
375 }
const char * getData() const
Definition: StdBuf.h:450
int iBPC
Definition: StdPNG.h:45
DWORD GetPix(int iX, int iY)
Definition: StdPNG.cpp:173
bool LogSilent(const char *szMessage, bool fConsole)
Definition: C4Log.cpp:117
#define GetGreenValue(rgb)
Definition: StdColors.h:30
virtual bool IsSelfDestruct()
Definition: StdPNG.cpp:318
static void WaitForSaves()
Definition: StdPNG.cpp:360
int iIntrlcType
Definition: StdPNG.h:45
int iCmprType
Definition: StdPNG.h:45
#define GetRedValue(rgb)
Definition: StdColors.h:31
static void ScheduleSaving(CPNGFile *png, const char *filename)
Definition: StdPNG.cpp:352
static bool HasPendingThreads()
Definition: StdPNG.cpp:346
bool Start()
void Clear()
Definition: StdPNG.cpp:136
void ClearPngStructs()
Definition: StdPNG.cpp:121
int iClrType
Definition: StdPNG.h:45
unsigned long iHgt
Definition: StdPNG.h:44
#define GetBlueValue(rgb)
Definition: StdColors.h:29
CPNGSaveThread(CPNGFile *png, const char *filename)
Definition: StdPNG.cpp:324
void SignalStop()
CPNGFile()
Definition: StdPNG.cpp:96
#define C4RGB(r, g, b)
Definition: StdColors.h:28
~CPNGFile()
Definition: StdPNG.cpp:115
bool Create(int iWdt, int iHgt, bool fAlpha)
Definition: StdPNG.cpp:189
void Default()
Definition: StdPNG.cpp:101
unsigned long iWdt
Definition: StdPNG.h:44
int iFltrType
Definition: StdPNG.h:45
bool SetPix(int iX, int iY, DWORD dwValue)
Definition: StdPNG.cpp:214
virtual void Execute()
Definition: StdPNG.cpp:338
uint32_t RGBA(uint32_t r, uint32_t g, uint32_t b, uint32_t a)
Definition: StdColors.h:24
uint32_t DWORD
bool Load(BYTE *pFile, int iSize)
Definition: StdPNG.cpp:153
virtual ~CPNGSaveThread()
Definition: StdPNG.cpp:331
bool Save(const char *szFilename)
Definition: StdPNG.cpp:234
int iSize
Definition: TstC4NetIO.cpp:35
int GetBitsPerPixel()
Definition: StdPNG.cpp:290