-
Notifications
You must be signed in to change notification settings - Fork 4
/
ooc-mipmap.jc
340 lines (332 loc) · 9.83 KB
/
ooc-mipmap.jc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
//do it the RA way - downsample, pack (in clamping mode), tile, zip
/*
or the simpler one texture per image mode? we only need one mipmap layer at any given time...
and the re-uploading time is the same anyway
in the OOM case, we just load higher mipmap levels
that requires to mass-new / mass-delete textures
which is easier than the tiling shit...
*/
import "javascript.jc"
import "gui2d.jc"
import "g-l.jc"
import System.Math.*
import System.Algorithm.*
import System.Console.*
import Gui2D.detail.*
import Gui2D.*
import Javascript.*
import GL.*
class CRGBAImage
int[] rgba
int w,h
///////////
GLuint texid
int is_used
float u_max,v_max
///////////
auto SavePNG()
len=0
spng=stbi_write_png_to_mem(rgba,w*4,w,h,4,&len)
if !spng:return string.NULL
ret=new string
ret.d=iptr(spng)
ret.n=iptr(len)
ret.sz=ret.n
return ret
auto downSampleMipmap(CRGBAImage img)
picbuf=new(img.rgba)
w0=img.w
h0=img.h
w=(w0+1)>>1
h=(h0+1)>>1
pbox=new int[w*h]
ppt=0
if w*2==w0:
for y=0:h0-1
pret=ppt
for i=0:w-1
C0=u32(picbuf[ppt])
C1=u32(picbuf[ppt+1])
picbuf[pret]=int(((C0&0xfefefefeu)>>1)+((C1&0xfefefefeu)>>1)+(C0&C1&0x01010101u))
pret++;ppt+=2
else
wit=1.f/float(2*w-1)
wt=float(w-1)/float(2*w-1)
wt1=1.f-wt//float(w)/float(2*w-1)
for y=0:h0-1
ppt=y*w0 // xb=0
pret=ppt
C0=u32(picbuf[ppt])
C1=u32(picbuf[ppt+1])
C=0
for i in crange(4)
C+=int(f32((C0>>(i*8))&255u)*wt1+f32((C1>>(i*8))&255u)*wt)<<(i*8)
picbuf[pret]=C
ppt+=2;wt0=wit
for xb=1:w-2
C0=u32(picbuf[ppt-1])
C1=u32(picbuf[ppt])
C2=u32(picbuf[ppt+1])
wt2=wt-wt0
C=0
for i in crange(4)
C+=int(f32((C0>>(i*8))&255u)*wt0+f32((C1>>(i*8))&255u)*wt1+f32((C1>>(i*8))&255u)*wt2)<<(i*8)
picbuf[pret]=C
pret++; ppt+=2; wt0+=wit
C0=u32(picbuf[ppt-1])
C1=u32(picbuf[ppt])
C=0
for i in crange(4)
C+=int(f32((C0>>(i*8))&255u)*wt+f32((C1>>(i*8))&255u)*wt1)<<(i*8)
picbuf[pret]=C
/////////////
ppt=0
if h*2==h0:
pret=0
for yb=0:h-1
ppt=(yb<<1)*w0
for xb=0:w-1
C0=u32(picbuf[ppt])
C1=u32(picbuf[ppt+w0])
pbox[pret]=int(((C0&0xfefefefeu)>>1)+((C1&0xfefefefeu)>>1)+(C0&C1&0x01010101u))
pret++;ppt++
else
wit=1.f/float(2*h-1)
wt=float(h-1)/float(2*h-1)
wt1=1.f-wt
pret=0; ppt=0
for xb=0:w-1
C0=u32(picbuf[ppt])
C1=u32(picbuf[ppt+w0])
C=0
for i in crange(4)
C+=int(f32((C0>>(i*8))&255u)*wt1+f32((C1>>(i*8))&255u)*wt)<<(i*8)
pbox[pret]=C
pret++;ppt++
wt0=wit
for yb=1:h-2
ppt=(yb<<1)*w0
for xb=0:w-1
C0=u32(picbuf[ppt-w0])
C1=u32(picbuf[ppt])
C2=u32(picbuf[ppt+w0])
wt2=wt-wt0
C=0
for i in crange(4)
C+=int(f32((C0>>(i*8))&255u)*wt0+f32((C1>>(i*8))&255u)*wt1+f32((C1>>(i*8))&255u)*wt2)<<(i*8)
pbox[pret]=C
pret++; ppt++
wt0+=wit
ppt=((h-1)<<1)*w0
for xb=0:w-1
C0=u32(picbuf[ppt-w0])
C1=u32(picbuf[ppt])
C=0
for i in crange(4)
C+=int(f32((C0>>(i*8))&255u)*wt+f32((C1>>(i*8))&255u)*wt1)<<(i*8)
pbox[pret]=C
pret++; ppt++
return new CRGBAImage(){rgba:pbox,w:w,h:h}
class CRequestedMipmapLevel
CEmbeddedImage img
int lv_min
float x,y,w,h, u_max,v_max
GLuint texid
class CEmbeddedImageManager
m_requests=new CRequestedMipmapLevel[]
m_budget=64<<20
m_tech=new GLTechnique
m_prev_cached_images=new CRGBAImage[]
m_cached_images=new CRGBAImage[]
auto BeginFrame()
m_requests.clear()
auto DrawMipmapAt(CEmbeddedImage img,float x,float y,float w,float h)
w0=img.m_mipmap[0].w
h0=img.m_mipmap[0].h
zoom_out=max(f32(w0)/w,f32(h0)/h)
lv_min=max(((__float_as_int(zoom_out))>>23)-127,0)
ctx=CRequestedMipmapLevel(){img:img,lv_min:lv_min,x:x,y:y,w:w,h:h}
window_w=g_renderer.m_window_w
window_h=g_renderer.m_window_h
g_renderer.InsertNativeDrawCall(function(){
if !ctx.texid:return
mini_vbo=[x,y,0.f,0.f, x+w,y,ctx.u_max,0.f, x+w,y+h,ctx.u_max,ctx.v_max, x,y+h,0.f,ctx.v_max]
m_tech.SetVarying("vec2 uv;")
m_tech.SetVertexShader("
void main(){
vec2 P_scr=P*scale.xy;
uv=uv_vert;
gl_Position=vec4(P_scr.x-1.0,1.0-P_scr.y,1.0,1.0);
}")
if g_renderer.m_srgb_supported:
m_tech.SetFragmentShader("void main(){vec4 C_tex=texture2D(tex0,uv);gl_FragColor=vec4(pow(C_tex.xyz,vec3("+formatNumber(g_renderer.m_gamma,{frac:7})+")),C_tex.w);}")
else
m_tech.SetFragmentShader("void main(){gl_FragColor=texture2D(tex0,uv);}")
m_tech.SetVertexPointer("P", 2,GL_FLOAT,0, 4*sizeof(float),mini_vbo.d)
m_tech.SetVertexPointer("uv_vert", 2,GL_FLOAT,0, 4*sizeof(float),mini_vbo.d+2*sizeof(float))
m_tech.SetUniform("scale",float2(2.f/f32(window_w),2.f/f32(window_h)))
m_tech.SetTexture2D("tex0", ctx.texid)
m_tech.Draw(0u,GL_QUADS,4)
mini_vbo.discard()
})
m_requests.push(ctx)
auto OnFirstDraw()
delta=0
for(;;)
mem=0L
foreach req in m_requests
mem+=req.img.GetMipmapMemory(req.lv_min+delta)
if mem<m_budget:
break
delta++
if delta>=16:
break
//single-frame cache
m_prev_cached_images=m_cached_images
m_cached_images=new CRGBAImage[]
foreach img in m_prev_cached_images
img.is_used=0
foreach req,I in m_requests
//Writeln('level ',req.lv_min+delta)
mipmap_img=req.img.RequestMipmap(req.lv_min+delta)
if mipmap_img.texid:
if !mipmap_img.is_used:
mipmap_img.is_used=1
m_cached_images.push(mipmap_img)
else
mipmap_img.texid=glCreateTexture(g_renderer.m_srgb_supported?GL_SRGB_ALPHA:GL_RGBA8,
mipmap_img.w,mipmap_img.h,GL_LINEAR,GL_CLAMP_TO_EDGE,GL_CLAMP_TO_EDGE,__pointer(mipmap_img.rgba.d))
mipmap_img.u_max=1.f
mipmap_img.v_max=1.f
//the power of 2 limitation...
if !mipmap_img.texid:
mipmap_img.texid=glCreateTexture(g_renderer.m_srgb_supported?GL_SRGB_ALPHA:GL_RGBA8,
promoteToTextureSize(mipmap_img.w),promoteToTextureSize(mipmap_img.h),
GL_LINEAR,GL_CLAMP_TO_EDGE,GL_CLAMP_TO_EDGE,NULL)
if mipmap_img.texid:
glTexSubImage2D(GL_TEXTURE_2D,0,0,0,mipmap_img.w,mipmap_img.h,g_renderer.m_srgb_supported?GL_SRGB_ALPHA:GL_RGBA8,GL_UNSIGNED_BYTE,__pointer(mipmap_img.rgba.d))
if g_renderer.m_srgb_supported&&glGetError()!=GL_NO_ERROR:
glTexSubImage2D(GL_TEXTURE_2D,0,0,0,mipmap_img.w,mipmap_img.h,GL_RGBA,GL_UNSIGNED_BYTE,__pointer(mipmap_img.rgba.d))
mipmap_img.u_max=float(mipmap_img.w)/float(promoteToTextureSize(mipmap_img.w))
mipmap_img.v_max=float(mipmap_img.h)/float(promoteToTextureSize(mipmap_img.h))
m_cached_images.push(mipmap_img)
req.texid=mipmap_img.texid
req.u_max=mipmap_img.u_max
req.v_max=mipmap_img.v_max
foreach img in m_prev_cached_images
if !img.is_used:
tid=img.texid
glDeleteTextures(1,&tid)
img.texid=0u
g_manager=new CEmbeddedImageManager
registerRenderingHook(g_manager)
class CEmbeddedImage
m_mipmap=new CRGBAImage[]
string m_raw_image
int w,h
auto GetMipmapMemory(int level)
img=m_mipmap[0]
w=img.w;h=img.h
for i=0:level-1
w=(w+1)>>1
h=(h+1)>>1
return iptr(w*h)
auto RequestMipmap(int level)
while m_mipmap.n<=level
img=m_mipmap.back()
if img.w==1||img.h==1:
return img
m_mipmap.push(downSampleMipmap(img))
return m_mipmap[level]
/////////////////////
__unique_id=-1.0
//can_keep_when_copied=1
//default_extension="png"
//JS_RenderAsObject=function(JSContext JS){
// g_manager.DrawMipmapAt(this,JS.Param(0).or(0.f),JS.Param(1).or(0.f),JS.Param(2).or(1.f),JS.Param(3).or(1.f))
// return 0
//}
JS_AsWidget=function(JSContext JS){
obj=JS.Param(1).as(JSObject)
if !obj:return 0
auto x=obj["x"].as(float)*g_renderer.m_pixels_per_unit
auto y=obj["y"].as(float)*g_renderer.m_pixels_per_unit
auto w=obj["w"].as(float)*g_renderer.m_pixels_per_unit
auto h=obj["h"].as(float)*g_renderer.m_pixels_per_unit
g_manager.DrawMipmapAt(this,x,y,w,h)
return 0
}
JS_GetReferences=function(JSContext JS){
return 0
}
JS_Save=function(JSContext JS){
if m_raw_image:
return JS.Return(m_raw_image)
else
return JS.Return(m_mipmap[0].SavePNG())
}
__JS_prototype=function(JSObject proto){
proto.ExportProperty(this,"w")
proto.ExportProperty(this,"h")
proto.ExportProperty(this,"__unique_id")
proto["default_extension"]="png"
//proto["share_across_docs"]=1
//proto.ExportMethod(this,"RenderAsObject",JS_RenderAsObject)
proto.ExportMethod(this,"AsWidget",JS_AsWidget)
proto.ExportMethod(this,"GetReferences",JS_GetReferences)
proto.ExportMethod(this,"Save",JS_Save)
}
//hotkey + win dlg first
if Platform.IS_WINDOWS:
osal_PickImageWin=__c_function(int,"osal_PickImageWin","filedlg.h","c_files","filedlg.c")
__generate_json("lib_files","comdlg32.lib")
string g_pickimage_filename
auto PickImage()
if !g_pickimage_filename:
s_userdir=System.Env.ExpandEnvironmentStrings(string("%USERPROFILE%"))
if !s_userdir.n||s_userdir[0]=='%':
s_userdir=System.Env.ExpandEnvironmentStrings(string("%SystemDrive%%HOMEPATH%"))
s_userdir.push("\\Pictures")
if !System.IO.DirExists(s_userdir):
//no "My Pictures"
s_userdir=new(".")
s_userdir.push('\\*')
g_pickimage_filename=s_userdir
buf=System.Algorithm.Utf8ToUnicode(g_pickimage_filename)
if buf.n<260:buf.resize(260)
if osal_PickImageWin(buf):
s=System.Algorithm.UnicodeToUtf8(buf)
pz=s.IndexOf(char(0))
if pz>=0:
s.resize(pz)
g_pickimage_filename=new(s)
return s
else
return string.NULL
else
auto PickImage()
//todo
return string.NULL
registerUIExtension(function(JSObject JS_UI,CUISandbox sbox,int is_real){
JS_UI["CreateEmbeddedImageFromFileData"]=function(JSContext JS){
s_fdata=JS.Param(0).as(string)
if !s_fdata:return 0
bmp=g_renderer.LoadImage(s_fdata)
if !bmp.rgba:return 0
ret=new CEmbeddedImage
if !s_fdata.StartsWith("BM"):
//keep the original representation unless it's a BMP
ret.m_raw_image=s_fdata
ret.m_mipmap=[new CRGBAImage(){rgba:bmp.rgba,w:bmp.w,h:bmp.h}]
ret.w=bmp.w
ret.h=bmp.h
return JS.Return(ret)
}
JS_UI["PickImage"]=function(JSContext JS){
ret=PickImage()
if !ret:
return 0
else
return JS.Return(ret)
}
})