Panda3D
 All Classes Functions Variables Enumerations
pnmFileTypeAndroidReader.cxx
1 // Filename: pnmFileTypeAndroidReader.cxx
2 // Created by: rdb (22Jan13)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "pnmFileTypeAndroid.h"
16 
17 #ifdef ANDROID
18 
19 #include "config_pnmimagetypes.h"
20 #include "config_express.h"
21 
22 #include <android/bitmap.h>
23 #include <jni.h>
24 
25 // These tables linearly map 4-bit, 5-bit or 6-bit to 8-bit values.
26 static uint8_t scale_table_4[16];
27 static uint8_t scale_table_5[32];
28 static uint8_t scale_table_6[64];
29 
30 static void init_scale_tables() {
31  static bool initialized = false;
32  if (!initialized) {
33  int i;
34  for (i = 0; i < 16; ++i) {
35  scale_table_4[i] = 255 * i / 15;
36  }
37  for (i = 0; i < 32; ++i) {
38  scale_table_5[i] = 255 * i / 31;
39  }
40  for (i = 0; i < 64; ++i) {
41  scale_table_6[i] = 255 * i / 63;
42  }
43  initialized = true;
44  }
45 }
46 
47 static void conv_rgb565(uint16_t in, xel &out) {
48  out.r = scale_table_5[(in >> 11) & 31];
49  out.g = scale_table_6[(in >> 5) & 63];
50  out.b = scale_table_5[in & 31];
51 }
52 
53 static void conv_rgba4444(uint16_t in, xel &rgb, xelval &alpha) {
54  rgb.r = scale_table_4[(in >> 12) & 0xF];
55  rgb.g = scale_table_4[(in >> 8) & 0xF];
56  rgb.b = scale_table_4[(in >> 4) & 0xF];
57  alpha = scale_table_4[in & 0xF];
58 }
59 
60 ////////////////////////////////////////////////////////////////////
61 // Function: PNMFileTypeAndroid::Reader::Constructor
62 // Access: Public
63 // Description:
64 ////////////////////////////////////////////////////////////////////
65 PNMFileTypeAndroid::Reader::
66 Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) :
67  PNMReader(type, file, owns_file), _bitmap(NULL)
68 {
69  // Hope we can putback() more than one character.
70  for (string::reverse_iterator mi = magic_number.rbegin();
71  mi != magic_number.rend(); ++mi) {
72  _file->putback(*mi);
73  };
74  if (_file->fail()) {
75  android_cat.error()
76  << "Unable to put back magic number.\n";
77  _is_valid = false;
78  return;
79  }
80 
81  streampos pos = _file->tellg();
82  _env = get_jni_env();
83  jobject opts = _env->CallStaticObjectMethod(jni_PandaActivity,
84  jni_PandaActivity_readBitmapSize,
85  (jlong) _file);
86  _file->seekg(pos);
87  if (_file->tellg() != pos) {
88  android_cat.error()
89  << "Unable to seek back to beginning.\n";
90  _is_valid = false;
91  return;
92  }
93 
94  _x_size = _env->GetIntField(opts, jni_BitmapFactory_Options_outWidth);
95  _y_size = _env->GetIntField(opts, jni_BitmapFactory_Options_outHeight);
96 
97  if (_x_size < 0 || _y_size < 0) {
98  android_cat.error()
99  << "Failed to read header of " << *this << "\n";
100  _is_valid = false;
101  }
102 
103  // Apparently we have to know this even though we don't yet.
104  _num_channels = 4;
105  _maxval = 255;
106 
107  if (android_cat.is_debug()) {
108  android_cat.debug()
109  << "Reading " << *this << "\n";
110  }
111 }
112 
113 ////////////////////////////////////////////////////////////////////
114 // Function: PNMFileTypeAndroid::Reader::Destructor
115 // Access: Public, Virtual
116 // Description:
117 ////////////////////////////////////////////////////////////////////
118 PNMFileTypeAndroid::Reader::
119 ~Reader() {
120  if (_bitmap != NULL) {
121  _env->DeleteGlobalRef(_bitmap);
122  }
123 }
124 
125 ////////////////////////////////////////////////////////////////////
126 // Function: PNMFileTypeAndroid::Reader::prepare_read
127 // Access: Public, Virtual
128 // Description: This method will be called before read_data() or
129 // read_row() is called. It instructs the reader to
130 // initialize its data structures as necessary to
131 // actually perform the read operation.
132 //
133 // After this call, _x_size and _y_size should reflect
134 // the actual size that will be filled by read_data()
135 // (as possibly modified by set_read_size()).
136 ////////////////////////////////////////////////////////////////////
137 void PNMFileTypeAndroid::Reader::
138 prepare_read() {
139  _sample_size = 2;
140  _orig_x_size = _x_size;
141  _orig_y_size = _y_size;
142 
143  if (_has_read_size && _read_x_size != 0 && _read_y_size != 0) {
144  int x_reduction = _orig_x_size / _read_x_size;
145  int y_reduction = _orig_y_size / _read_y_size;
146 
147  _sample_size = max(min(x_reduction, y_reduction), 1);
148  }
149 
150  _bitmap = _env->CallStaticObjectMethod(jni_PandaActivity,
151  jni_PandaActivity_readBitmap,
152  (jlong) _file, _sample_size);
153 
154  if (_bitmap == NULL) {
155  android_cat.error()
156  << "Failed to read " << *this << "\n";
157  _is_valid = false;
158  return;
159  }
160 
161  _bitmap = _env->NewGlobalRef(_bitmap);
162 
163  AndroidBitmapInfo info;
164  if (AndroidBitmap_getInfo(_env, _bitmap, &info) < 0) {
165  android_cat.error()
166  << "Failed to get info of " << *this << "\n";
167  _is_valid = false;
168  return;
169  }
170 
171  _x_size = info.width;
172  _y_size = info.height;
173  _format = info.format;
174  _stride = info.stride;
175 
176  // Note: we could be setting maxval more appropriately,
177  // but this only causes texture.cxx to end up rescaling it later.
178  // Best to do the scaling ourselves, using efficient tables.
179  _maxval = 255;
180 
181  switch (info.format) {
182  case ANDROID_BITMAP_FORMAT_RGBA_8888:
183  _num_channels = 4;
184  android_cat.debug()
185  << "Bitmap has format RGBA_8888\n";
186  break;
187  case ANDROID_BITMAP_FORMAT_RGB_565:
188  _num_channels = 3;
189  android_cat.debug()
190  << "Bitmap has format RGB_565\n";
191  break;
192  case ANDROID_BITMAP_FORMAT_RGBA_4444:
193  _num_channels = 4;
194  android_cat.debug()
195  << "Bitmap has format RGBA_4444\n";
196  break;
197  case ANDROID_BITMAP_FORMAT_A_8:
198  _num_channels = 1;
199  android_cat.debug()
200  << "Bitmap has format A_8\n";
201  break;
202  default:
203  android_cat.error()
204  << "Unsupported bitmap format!\n";
205  _num_channels = 0;
206  _is_valid = false;
207  break;
208  }
209 }
210 
211 ////////////////////////////////////////////////////////////////////
212 // Function: PNMFileTypeAndroid::Reader::read_data
213 // Access: Public, Virtual
214 // Description: Reads in an entire image all at once, storing it in
215 // the pre-allocated _x_size * _y_size array and alpha
216 // pointers. (If the image type has no alpha channel,
217 // alpha is ignored.) Returns the number of rows
218 // correctly read.
219 //
220 // Derived classes need not override this if they
221 // instead provide supports_read_row() and read_row(),
222 // below.
223 ////////////////////////////////////////////////////////////////////
224 int PNMFileTypeAndroid::Reader::
225 read_data(xel *rgb, xelval *alpha) {
226  if (!_is_valid) {
227  return 0;
228  }
229  void *ptr;
230  if (AndroidBitmap_lockPixels(_env, _bitmap, &ptr) < 0) {
231  android_cat.error()
232  << "Failed to lock bitmap for reading.\n";
233  return 0;
234  }
235 
236  switch (_format) {
237  case ANDROID_BITMAP_FORMAT_RGBA_8888: {
238  nassertr(_stride == _x_size * 4, 0);
239  uint8_t *data = (uint8_t *) ptr;
240  for (int y = 0; y < _y_size; ++y) {
241  for (int x = 0; x < _x_size; ++x) {
242  rgb[x].r = data[0];
243  rgb[x].g = data[1];
244  rgb[x].b = data[2];
245  alpha[x] = data[3];
246  data += 4;
247  }
248  rgb += _x_size;
249  alpha += _y_size;
250  }
251  break;
252  }
253  case ANDROID_BITMAP_FORMAT_RGB_565: {
254  nassertr(_stride == _x_size * 2, 0);
255  init_scale_tables();
256 
257  uint16_t *data = (uint16_t *) ptr;
258  for (int y = 0; y < _y_size; ++y) {
259  for (int x = 0; x < _x_size; ++x) {
260  conv_rgb565(data[x], rgb[x]);
261  }
262  data += _x_size;
263  rgb += _x_size;
264  }
265  break;
266  }
267  case ANDROID_BITMAP_FORMAT_RGBA_4444: {
268  nassertr(_stride == _x_size * 2, 0);
269  init_scale_tables();
270 
271  uint16_t *data = (uint16_t *) ptr;
272  for (int y = 0; y < _y_size; ++y) {
273  for (int x = 0; x < _x_size; ++x) {
274  conv_rgba4444(data[x], rgb[x], alpha[x]);
275  }
276  data += _x_size;
277  rgb += _x_size;
278  alpha += _x_size;
279  }
280  break;
281  }
282  case ANDROID_BITMAP_FORMAT_A_8: {
283  nassertr(_stride == _x_size, 0);
284  uint8_t *data = (uint8_t *) ptr;
285  for (int y = 0; y < _y_size; ++y) {
286  for (int x = 0; x < _x_size; ++x) {
287  alpha[x] = data[x];
288  }
289  data += _x_size;
290  alpha += _x_size;
291  }
292  break;
293  }
294  default:
295  AndroidBitmap_unlockPixels(_env, _bitmap);
296  return 0;
297  }
298 
299  AndroidBitmap_unlockPixels(_env, _bitmap);
300  return _y_size;
301 }
302 
303 #endif // ANDROID
This is the base class of a family of classes that represent particular image file types that PNMImag...
Definition: pnmFileType.h:35
This is an abstract base class that defines the interface for reading image files of various types...
Definition: pnmReader.h:31