【发布时间】:2016-03-24 19:08:07
【问题描述】:
我正在尝试创建一个函数,该函数将采用 BufferedImage 并返回一个 ByteBuffer,然后我可以将其用作 OpenGL 纹理。为此,我了解到我必须进行一些与我的问题无关的字节移位。它与 BufferedImage 值是 ARGB 和 OpenGL 想要 RGBA 有关。
我试图实现的功能(来自java)是这个:
public static ByteBuffer toByteBuffer(BufferedImage img){
byte[] byteArray = new byte[img.getWidth()*img.getHeight()*4];
for(int i = 0; i < img.getWidth()*img.getHeight(); i++){
int value = img.getRGB(i%img.getWidth(), (i-(i%img.getWidth()))/img.getWidth() );
byteArray[i*4] = (byte) ((value<<8)>>24);
byteArray[i*4+1] = (byte) ((value<<16)>>24);
byteArray[i*4+2] = (byte) ((value<<24)>>24);
byteArray[i*4+3] = (byte) (value>>24);
}
return (ByteBuffer) ByteBuffer.allocateDirect(byteArray.length).put(byteArray).flip();
}
这是我对 clojure 的尝试:
(defn sub-byte [^long b ^long x]
(unchecked-byte (-> x
(bit-shift-left (* 8 b))
(bit-shift-right 24))))
(defn bufferedimage->bytebuffer [^BufferedImage img]
(binding [*unchecked-math* true]
(let [w (.getWidth img)
h (.getHeight img)
^bytes arr (make-array Byte/TYPE (* 4 w h))]
(loop [i 0]
(let [img-i (mod i w)
img-j (quot i w)
value (.getRGB img img-i img-j)]
(aset arr (* i 4) (sub-byte 1 value))
(aset arr (+ 1 (* i 4)) (sub-byte 2 value))
(aset arr (+ 2 (* i 4)) (sub-byte 3 value))
(aset arr (+ 3 (* i 4)) (sub-byte 0 value))
(when (< (+ i 1) (* w h)) (recur (+ i 1)))
))
(cast ByteBuffer (-> (ByteBuffer/allocateDirect (count arr))
(.put arr)
(.flip))))))
加载一个 512*512 的图块集需要 10 秒,这是完全不能接受的。我正试图让这个运行在不到一秒的时间内完成。
请注意,一直占用的部分是循环。
我不妨提一下,这些时间是使用 REPL 记录的。
另外,请注意,我很清楚我可以将 java 用于我的代码的性能关键部分,所以这更多是一个理论问题,因此我可以学习如何优化我的 clojure 代码。
【问题讨论】:
-
您可以使用
clojure.core/time来衡量您的代码中哪些部分花费的时间最多。 -
我已经做到了。从我的帖子中:“请注意,一直占用的部分是循环。”。
-
Criterium 是一种更好的基准测试方法。一般来说,
map、reduce和filter可能是比loop/recur更好的速度选择和更惯用的选择。 -
我认为 map、reduce 和 filter 实际上会比尾调用优化循环更糟糕,因为构建惰性序列的开销,不是这样吗?
-
你为什么要用 byte[] 来做这个? ByteBuffer 是专门存在的,因此您不必做太多间接摆弄堆上的东西。在函数开始时分配一个大小正确的 ByteBuffer,然后多次调用
.put,而不是先创建一个 byte[] 然后再做一堆算术来计算出正确的偏移量!
标签: opengl optimization clojure lwjgl