+ Conditional<BindRestore> _bind(!ARB_direct_state_access, this);
+ allocate(level);
+
+ PixelComponents comp = get_components(storage_fmt);
+ DataType type = get_component_type(storage_fmt);
+ if(ARB_direct_state_access)
+ glTextureSubImage2D(id, level, x, y, wd, ht, comp, type, data);
+ else
+ glTexSubImage2D(target, level, x, y, wd, ht, comp, type, data);
+
+ if(auto_gen_mipmap && level==0)
+ generate_mipmap();
+}
+
+void Texture2D::sub_image(unsigned level, int x, int y, unsigned wd, unsigned ht, PixelComponents comp, DataType type, const void *data)
+{
+ if(comp!=get_components(format) || type!=get_component_type(format))
+ throw incompatible_data("Texture2D::sub_image");
+ sub_image(level, x, y, wd, ht, data);
+}
+
+void Texture2D::image(const Graphics::Image &img, unsigned lv)
+{
+ image(img, lv, false);
+}
+
+void Texture2D::image(const Graphics::Image &img, unsigned lv, bool from_buffer)
+{
+ unsigned w = img.get_width();
+ unsigned h = img.get_height();
+ PixelFormat fmt = pixelformat_from_image(img);
+ if(width==0)
+ storage(make_pixelformat(get_components(fmt), get_component_type(fmt), use_srgb_format), w, h, lv);
+ else if(w!=width || h!=height || (lv && lv!=levels))
+ throw incompatible_data("Texture2D::image");
+
+ PixelStore pstore = PixelStore::from_image(img);
+ BindRestore _bind_ps(pstore);
+
+ image(0, from_buffer ? 0 : img.get_pixels());
+}
+
+unsigned Texture2D::get_n_levels() const
+{
+ unsigned n = 0;
+ for(unsigned s=max(width, height); s; s>>=1, ++n) ;
+ return n;
+}
+
+void Texture2D::get_level_size(unsigned level, unsigned &w, unsigned &h) const
+{
+ w >>= level;
+ h >>= level;
+
+ if(!w && h)
+ w = 1;
+ else if(!h && w)
+ h = 1;
+}
+
+Resource::AsyncLoader *Texture2D::load(IO::Seekable &io, const Resources *)
+{
+ AsyncLoader *ldr = new AsyncLoader(*this, io);
+ return ldr;
+}