+#include <algorithm>
+#include <msp/gl/extensions/ext_texture3d.h>
+#include <msp/gl/extensions/ext_unpack_subimage.h>
+#include "gl.h"
+#include "pixelformat.h"
+#include "pixelstore.h"
+
+using namespace std;
+
+namespace Msp {
+namespace GL {
+
+PixelStore::PixelStore():
+ row_length(0),
+ image_height(0),
+ skip_pixels(0),
+ skip_rows(0),
+ skip_images(0),
+ alignment(4)
+{ }
+
+PixelStore PixelStore::from_image(const Graphics::Image &img)
+{
+ PixelStore pstore;
+ unsigned stride = img.get_stride();
+ unsigned ncomp = get_component_count(pixelformat_from_image(img));
+ pstore.set_canvas_size(img.get_stride()/ncomp, 0);
+ pstore.set_alignment(min(stride&~(stride-1), 8U));
+ return pstore;
+}
+
+void PixelStore::update_parameter(int mask) const
+{
+ if(cur_obj!=this)
+ return;
+
+ if(mask&SIZE)
+ {
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, row_length);
+ if(EXT_texture3D)
+ glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, image_height);
+ }
+ if(mask&ORIGIN)
+ {
+ glPixelStorei(GL_UNPACK_SKIP_PIXELS, skip_pixels);
+ glPixelStorei(GL_UNPACK_SKIP_ROWS, skip_rows);
+ if(EXT_texture3D)
+ glPixelStorei(GL_UNPACK_SKIP_IMAGES, skip_images);
+ }
+ if(mask&ALIGNMENT)
+ glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
+}
+
+void PixelStore::set_canvas_size(unsigned w, unsigned h)
+{
+ static Require _req(EXT_unpack_subimage);
+ row_length = w;
+ image_height = h;
+ update_parameter(SIZE);
+}
+
+void PixelStore::set_origin(unsigned x, unsigned y, unsigned z)
+{
+ static Require _req(EXT_unpack_subimage);
+ if(z>0)
+ static Require _req3d(EXT_texture3D);
+ skip_pixels = x;
+ skip_rows = y;
+ skip_images = z;
+ update_parameter(ORIGIN);
+}
+
+void PixelStore::set_alignment(unsigned a)
+{
+ alignment = a;
+ update_parameter(ALIGNMENT);
+}
+
+void PixelStore::bind() const
+{
+ if(set_current(this))
+ update_parameter(-1);
+}
+
+} // namespace GL
+} // namespace Msp