1 /*
2     Copyright (C) 2009 Johannes Schindelin (johannes.schindelin@gmx.de)
3 
4     This program is free software; you can redistribute it and/or
5     modify it under the terms of the GNU General Public License
6     as published by the Free Software Foundation; either version
7     3 of the License, or (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 */
17 
18 #include "ppm.h"
19 #include "display.h"
20 #include "sdlapp.h"
21 
22 #ifdef _WIN32
23     #include <io.h>
24     #include <fcntl.h>
25 #endif
26 
27 extern "C" {
dumper_thread(void * arg)28 static int dumper_thread(void *arg) {
29     FrameExporter *e = static_cast<FrameExporter *>(arg);
30 
31     e->dumpThr();
32 
33     return 0;
34 }
35 };
36 
37 // FrameExporter
38 
FrameExporter()39 FrameExporter::FrameExporter() {
40 
41     //this now assumes the display is setup
42     //before the frame exporter is created
43     //(which seems reasonable)
44 
45     rowstride     = display.width * 3;
46 
47     pixels1        = new char[display.height * rowstride];
48     pixels2        = new char[display.height * rowstride];
49     pixels_out     = new char[display.height * rowstride];
50 
51     pixels_shared_ptr = 0;
52 
53     dumper_thread_state = FRAME_EXPORTER_WAIT;
54 
55     cond   = SDL_CreateCond();
56     mutex  = SDL_CreateMutex();
57 
58 #if SDL_VERSION_ATLEAST(2,0,0)
59     thread = SDL_CreateThread( dumper_thread, "frame_exporter", this );
60 #else
61     thread = SDL_CreateThread( dumper_thread, this );
62 #endif
63 }
64 
~FrameExporter()65 FrameExporter::~FrameExporter() {
66     stop();
67 
68     SDL_DestroyCond(cond);
69     SDL_DestroyMutex(mutex);
70 
71     pixels_shared_ptr = 0;
72 
73     delete[] pixels1;
74     delete[] pixels2;
75     delete[] pixels_out;
76 }
77 
stop()78 void FrameExporter::stop() {
79     if(!thread) return;
80 
81     if(dumper_thread_state == FRAME_EXPORTER_STOPPED || dumper_thread_state == FRAME_EXPORTER_EXIT) return;
82 
83     SDL_mutexP(mutex);
84 
85         dumper_thread_state = FRAME_EXPORTER_EXIT;
86 
87         SDL_CondSignal(cond);
88 
89     SDL_mutexV(mutex);
90 
91     SDL_WaitThread(thread, 0);
92 
93     thread = 0;
94 }
95 
dump()96 void FrameExporter::dump() {
97 
98     display.mode2D();
99 
100     glEnable(GL_TEXTURE_2D);
101     glDisable(GL_BLEND);
102 
103     char* next_pixel_ptr = (pixels_shared_ptr == pixels1) ? pixels2 : pixels1;
104 
105     // copy pixels - now the right way up
106     glPixelStorei(GL_PACK_ALIGNMENT, 1);
107     glReadPixels(0, 0, display.width, display.height,
108         GL_RGB, GL_UNSIGNED_BYTE, next_pixel_ptr);
109 
110     // wait for lock before changing the pointer to point to our new buffer
111     SDL_mutexP(mutex);
112 
113         //flip buffer we are pointing at
114         pixels_shared_ptr = next_pixel_ptr;
115         dumper_thread_state = FRAME_EXPORTER_DUMP;
116 
117     SDL_CondSignal(cond);
118     SDL_mutexV(mutex);
119 }
120 
dumpThr()121 void FrameExporter::dumpThr() {
122 
123     SDL_mutexP(mutex);
124 
125     while(dumper_thread_state != FRAME_EXPORTER_EXIT) {
126 
127         dumper_thread_state = FRAME_EXPORTER_WAIT;
128 
129         while (dumper_thread_state == FRAME_EXPORTER_WAIT) {
130             SDL_CondWait(cond, mutex);
131         }
132 
133         if (dumper_thread_state == FRAME_EXPORTER_EXIT) break;
134 
135         if (pixels_shared_ptr != 0) {
136 
137             //invert image
138             for(int y=0;y<display.height;y++) {
139                 for(int x=0;x<rowstride;x++) {
140                     pixels_out[x + y * rowstride] = pixels_shared_ptr[x + (display.height - y - 1) * rowstride];
141                 }
142             }
143 
144             dumpImpl();
145         }
146     }
147 
148     dumper_thread_state = FRAME_EXPORTER_STOPPED;
149 
150     SDL_mutexV(mutex);
151 
152 }
153 
154 // PPMExporter
155 
PPMExporter(std::string outputfile)156 PPMExporter::PPMExporter(std::string outputfile) {
157 
158     if(outputfile == "-") {
159 #ifdef _WIN32
160         _setmode( _fileno( stdout ), _O_BINARY );
161 #endif // _WIN32
162         output = &std::cout;
163 
164     } else {
165         filename = outputfile;
166         output   = new std::ofstream(outputfile.c_str(), std::ios::out | std::ios::binary);
167 
168         if(output->fail()) {
169             delete output;
170             throw PPMExporterException(outputfile);
171         }
172     }
173 
174     //write header
175     sprintf(ppmheader, "P6\n%d %d 255\n", display.width, display.height);
176 }
177 
~PPMExporter()178 PPMExporter::~PPMExporter() {
179     stop();
180 
181     if(filename.size()>0)
182         ((std::fstream*)output)->close();
183 }
184 
dumpImpl()185 void PPMExporter::dumpImpl() {
186     *output << ppmheader;
187     output->write(pixels_out, rowstride * display.height);
188 }
189