1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "PgmImage.h"
18#include <cassert>
19
20using namespace std;
21
22PgmImage::PgmImage(std::string filename) :
23m_w(0),m_h(0),m_colors(255),m_format(PGM_BINARY_GRAYMAP),m_over_allocation(256)
24{
25    if ( !ReadPGM(filename) )
26        return;
27}
28
29PgmImage::PgmImage(int w, int h, int format) :
30m_colors(255),m_w(w),m_h(h),m_format(format),m_over_allocation(256)
31{
32    SetFormat(format);
33}
34
35PgmImage::PgmImage(unsigned char *data, int w, int h) :
36m_colors(255),m_w(w),m_h(h),m_format(PGM_BINARY_GRAYMAP),m_over_allocation(256)
37{
38    SetData(data);
39}
40
41PgmImage::PgmImage(std::vector<unsigned char> &data, int w, int h) :
42m_colors(255),m_w(w),m_h(h),m_format(PGM_BINARY_GRAYMAP),m_over_allocation(256)
43{
44    if ( data.size() == w*h )
45        SetData(&data[0]);
46    else
47        //throw (std::exception("Size of data is not w*h."));
48        throw (std::exception());
49}
50
51PgmImage::PgmImage(const PgmImage &im) :
52m_colors(255),m_w(0),m_h(0),m_format(PGM_BINARY_GRAYMAP),m_over_allocation(256)
53{
54    DeepCopy(im, *this);
55}
56
57PgmImage& PgmImage::operator= (const PgmImage &im)
58{
59    if (this == &im) return *this;
60    DeepCopy(im, *this);
61    return *this;
62}
63
64void PgmImage::DeepCopy(const PgmImage& src, PgmImage& dst)
65{
66    dst.m_data = src.m_data;
67
68    // PGM data
69    dst.m_w = src.m_w;
70    dst.m_h = src.m_h;
71    dst.m_format = src.m_format;
72    dst.m_colors = src.m_colors;
73
74    dst.m_comment = src.m_comment;
75    SetupRowPointers();
76}
77
78PgmImage::~PgmImage()
79{
80
81}
82
83void PgmImage::SetFormat(int format)
84{
85    m_format = format;
86
87    switch (format)
88    {
89    case PGM_BINARY_GRAYMAP:
90        m_data.resize(m_w*m_h+m_over_allocation);
91        break;
92    case PGM_BINARY_PIXMAP:
93        m_data.resize(m_w*m_h*3+m_over_allocation);
94        break;
95    default:
96        return;
97        break;
98    }
99    SetupRowPointers();
100}
101
102void PgmImage::SetData(const unsigned char * data)
103{
104    m_data.resize(m_w*m_h+m_over_allocation);
105    memcpy(&m_data[0],data,m_w*m_h);
106    SetupRowPointers();
107}
108
109bool PgmImage::ReadPGM(const std::string filename)
110{
111    ifstream in(filename.c_str(),std::ios::in | std::ios::binary);
112    if ( !in.is_open() )
113        return false;
114
115    // read the header:
116    string format_header,size_header,colors_header;
117
118    getline(in,format_header);
119    stringstream s;
120    s << format_header;
121
122    s >> format_header >> m_w >> m_h >> m_colors;
123    s.clear();
124
125    if ( m_w == 0 )
126    {
127        while ( in.peek() == '#' )
128            getline(in,m_comment);
129
130        getline(in,size_header);
131
132        while ( in.peek() == '#' )
133            getline(in,m_comment);
134
135            m_colors = 0;
136
137        // parse header
138        s << size_header;
139        s >> m_w >> m_h >> m_colors;
140        s.clear();
141
142        if ( m_colors == 0 )
143        {
144            getline(in,colors_header);
145            s << colors_header;
146            s >> m_colors;
147        }
148    }
149
150    if ( format_header == "P5" )
151        m_format = PGM_BINARY_GRAYMAP;
152    else if (format_header == "P6" )
153        m_format = PGM_BINARY_PIXMAP;
154    else
155        m_format = PGM_FORMAT_INVALID;
156
157    switch(m_format)
158    {
159    case(PGM_BINARY_GRAYMAP):
160        m_data.resize(m_w*m_h+m_over_allocation);
161        in.read((char *)(&m_data[0]),m_data.size());
162        break;
163    case(PGM_BINARY_PIXMAP):
164        m_data.resize(m_w*m_h*3+m_over_allocation);
165        in.read((char *)(&m_data[0]),m_data.size());
166        break;
167    default:
168        return false;
169        break;
170    }
171    in.close();
172
173    SetupRowPointers();
174
175    return true;
176}
177
178bool PgmImage::WritePGM(const std::string filename, const std::string comment)
179{
180    string format_header;
181
182    switch(m_format)
183    {
184    case PGM_BINARY_GRAYMAP:
185        format_header = "P5\n";
186        break;
187    case PGM_BINARY_PIXMAP:
188        format_header = "P6\n";
189        break;
190    default:
191        return false;
192        break;
193    }
194
195    ofstream out(filename.c_str(),std::ios::out |ios::binary);
196    out << format_header << "# " << comment << '\n' << m_w << " " << m_h << '\n' << m_colors << '\n';
197
198    out.write((char *)(&m_data[0]), m_data.size());
199
200    out.close();
201
202    return true;
203}
204
205void PgmImage::SetupRowPointers()
206{
207    int i;
208    m_rows.resize(m_h);
209
210    switch (m_format)
211    {
212    case PGM_BINARY_GRAYMAP:
213        for(i=0;i<m_h;i++)
214        {
215            m_rows[i]=&m_data[m_w*i];
216        }
217        break;
218    case PGM_BINARY_PIXMAP:
219        for(i=0;i<m_h;i++)
220        {
221            m_rows[i]=&m_data[(m_w*3)*i];
222        }
223        break;
224    }
225}
226
227void PgmImage::ConvertToGray()
228{
229    if ( m_format != PGM_BINARY_PIXMAP ) return;
230
231    // Y = 0.3*R + 0.59*G + 0.11*B;
232    for ( int i = 0; i < m_w*m_h; ++i )
233        m_data[i] = (unsigned char)(0.3*m_data[3*i]+0.59*m_data[3*i+1]+0.11*m_data[3*i+2]);
234
235    m_data.resize(m_w*m_h+m_over_allocation);
236    m_format = PGM_BINARY_GRAYMAP;
237
238    SetupRowPointers();
239}
240
241std::ostream& operator<< (std::ostream& o, const PgmImage& im)
242{
243    o << "PGM Image Info:\n";
244    o << "Size: " << im.m_w << " x " << im.m_h << "\n";
245    o << "Comment: " << im.m_comment << "\n";
246    switch (im.m_format)
247    {
248    case PgmImage::PGM_BINARY_PIXMAP:
249        o << "Format: RGB binary pixmap";
250        break;
251    case PgmImage::PGM_BINARY_GRAYMAP:
252        o << "Format: PPM binary graymap";
253        break;
254    default:
255        o << "Format: Invalid";
256        break;
257    }
258    o << endl;
259    return o;
260}
261