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