import java.io._
object Bmp24Writer {
//将加密的数据写入文件
def writeEncryptedBmp(bmpPath: String, keys: Array[Int],
shift: Int, times: Int,
red: Array[Array[Int]],
green: Array[Array[Int]],
blue: Array[Array[Int]]) = {
val width = red(0).length
val fos = new FileOutputStream(bmpPath)
val dos = new DataOutputStream(fos)
writeBmp24(dos, red, green ,blue)
for(i <- 0 until width)
print(s"${keys(i)} ")
println
println(s"times: $times, shift $shift")
for(i <- 0 until width)
dos write(changeByte(keys(i)), 0, 4)
dos write(changeByte(times), 0, 4)
dos write(changeByte(shift), 0, 4)
dos.flush
dos.close
dos.close
}
//将解密后的数据写入文件
def writeNormalBmp(bmpPath: String,
red: Array[Array[Int]],
green: Array[Array[Int]],
blue: Array[Array[Int]]) = {
val fos = new FileOutputStream(bmpPath)
val dos = new DataOutputStream(fos)
writeBmp24(dos, red, green ,blue)
dos.flush
dos.close
fos.close
}
//写bmp图片的信息头和文件头还有位图数据
def writeBmp24(dos: DataOutputStream,
red: Array[Array[Int]],
green: Array[Array[Int]],
blue: Array[Array[Int]]) = {
val width = red(0).length
val height = red.length
//14字节的文件头
//代表BM
val bfType = 0x424d.toShort
// bmp文件的大小(2—5字节)
val bfSize = 54 + width * height * 3
// 位图文件保留字,必须为0(6-7字节)
val bfReserved1 = 0
// 位图文件保留字,必须为0(8-9字节)
val bfReserved2 = 0
// 文件头开始到位图实际数据之间的字节的偏移量(10-13字节)
val bfOffBits = 54
dos writeShort bfType
dos write(changeByte(bfSize), 0, 4)
dos write(changeByte(bfReserved1), 0, 2)
dos write(changeByte(bfReserved2), 0, 2)
dos write(changeByte(bfOffBits), 0, 4)
//40字节的信息头
// 信息头所需的字节数(14-17字节)
val biSize = 40
// 位图的宽(18-21字节)
val biWidth = width
// 位图的高(22-25字节)
val biHeight = height
// 目标设备的级别,必须是1(26-27字节)
val biPlanes = 1
// 每个像素所需的位数(28-29字节),必须是1位(双色)、
// 4位(16色)、8位(256色)或者24位(真彩色)之一。
val biBitcount = 24
// 位图压缩类型,必须是0(不压缩)(30-33字节)、
//1(BI_RLEB压缩类型)或2(BI_RLE4压缩类型)之一。
val biCompression = 0
// 实际位图图像的大小,即整个实际绘制的图像大小(34-37字节)
val biSizeImage = width * height
// 位图水平分辨率,每米像素数(38-41字节)这个数是系统默认值
val biXPelsPerMeter = 0
// 位图垂直分辨率,每米像素数(42-45字节)这个数是系统默认值
val biYPelsPerMeter = 0
// 位图实际使用的颜色表中的颜色数(46-49字节),
// 如果为0的话,说明全部使用了
val biClrUsed = 0
// 位图显示过程中重要的颜色数(50-53字节),
// 如果为0的话,说明全部重要
val biClrImportant = 0
// 因为是大端存储,那么也就是说同样会大端输出。
// 所以首先调用方法将int数据转变为多个byte数据,
// 并且按小端存储的顺序.
dos write(changeByte(biSize), 0, 4)
dos write(changeByte(biWidth), 0, 4)
dos write(changeByte(biHeight), 0, 4)
dos write(changeByte(biPlanes), 0, 2)
dos write(changeByte(biBitcount), 0, 2)
dos write(changeByte(biCompression), 0, 4)
dos write(changeByte(biSizeImage), 0, 4)
dos write(changeByte(biXPelsPerMeter), 0, 4)
dos write(changeByte(biYPelsPerMeter), 0, 4)
dos write(changeByte(biClrUsed), 0, 4)
dos write(changeByte(biClrImportant), 0, 4)
// 因为是24位图,所以没有颜色表
// 通过遍历输入位图数据
// 这里遍历的时候注意,在计算机内存中位图数据
// 是从左到右,从下到上来保存的,
// 也就是说实际图像的第一行的点在内存是最后一行
for(i <- height - 1 to 0 by -1){
for(j <- 0 to width - 1){
dos write(changeByte(blue(i)(j)), 0, 1)
dos write(changeByte(green(i)(j)), 0, 1)
dos write(changeByte(red(i)(j)), 0, 1)
}
}
}
// 将一个int数据转为按小端顺序排列的字节数组
def changeByte(data: Int): Array[Byte] =
Array(
((data << 24) >> 24).toByte,
((data << 16) >> 24).toByte,
((data << 8) >> 24).toByte,
(data >> 24).toByte
)
// 将四个字节解析成int数据
def ChangeInt(array2: Array[Byte], start: Int) = {
val i =
((array2(start) & 0xff) << 24) | ((array2(start - 1) & 0xff) << 16)
val j =
((array2(start - 2) & 0xff) << 8) | (array2(start - 3) & 0xff)
i | j
}
// 读取rgb数据
def getInf(bis: BufferedInputStream, height: Int, width: Int) = {
val red = Array.ofDim[Int](height, width)
val green = Array.ofDim[Int](height, width)
val blue = Array.ofDim[Int](height, width)
// 计算系统在每行的填充的字节数
val m = width * 3 % 4
var skip_width = 0
if(m != 0)
skip_width = 4 - m
for (i <- height - 1 to 0 by -1) {
for (j <- 0 to width - 1) {
blue(i)(j) = bis.read
green(i)(j) = bis.read
red(i)(j) = bis.read
if (j == 0) {
bis skip(skip_width)
}
}
}
(red, green, blue)
}
}
© 著作权归作者所有
举报
打赏
0 赞
0 收藏
分享
加载中

其他人还在看
这是Windows DHCP最佳实践和技巧的最终指南。 如果您有任何最佳做法或技巧,请在下面的评论中发布它们。 在本指南(一)中,我将分享以下DHCP最佳实践和技巧。 不要将DHCP放在您的域控制器上 使用DHCP故障转移 中...
你好,我是A哥(YourBatman)。 日期/时间的处理是平时开发中非常常见的场景,若只是简单的格式化场景那就还好,一旦涉及到时区、跨地域跨时区时间转换场景,甚至当还有GMT时间、UTC时间等一堆概念堆上来的时候,总...
我是架构精进之路,点击上方“关注”,坚持每天为你分享技术干货,私信我回复“01”,送你一份程序员成长进阶大礼包。 面试常见的四种算法思想,全在这里了,今天带你一文了解。 1、贪心 贪心算法有很多经典的应用...
前言 ”面向接口编程“写 Java 的朋友耳朵已经可以听出干茧了吧,当然这个思想在 Java 中非常重要,甚至几乎所有的编程语言都需要,毕竟程序具有良好的扩展性、维护性谁都不能拒绝。 最近无意间看到了我刚开始写 ...
以下文章来源于公众号 「iconfontplus」 ,作者前端快爆 2020 终究是一个不平凡的一年,我们经历了太多坎坷与磨炼。甚至受 COVID-19 疫情的影响,Chrome 浏览器罕见的断更了,Chrome 82 就此缺席。但 Web 生态依旧...
在前端js中,this虽然只是其中的一个关键字而已,但是确实一个比较特殊的关键字。为什么说this在js关键字中很特殊呢,原因就在于this是函数在运行时自动生成的一个对象,this的指向在函数定义的时候不是确定的,只...
作者 | 张磊 来源|阿里巴巴云原生公众号 2020 年注定是不凡的。它在阴霾中开始,在惊叹中结束,也让未来变得更加扑朔迷离。那么,容器与云原生的 2020 年呢?你是否记得它是怎样开始的?它又将走向何方? Kubern...
一般情况下技术岗面试都需要经历面试和笔试部分,面试过程中主要采用问答的形式,一般没有完全固定的回答,主要是根据自己的工作经验应答面试官的问题,而笔试部分更注重基础知识以及问题的常规解决方案。下面IT技...
生产者消费者模式最核心的部分是生产者与消费者之间的特殊容器,而阻塞队列是特殊容器最常见的实现。JDK中定义了阻塞队列接口BlockingQueue,JDK通过该接口为我们提供了很多种阻塞队列的实现,其中包括本节的主角...
接着前几天的两篇文章,继续解析JVM面试问题,送给年后想要跳槽的小伙伴 万万没想到,面试中,连 ClassLoader类加载器 也能问出这么多问题..... 万万没想到,JVM内存区域的面试题也可以问的这么难? 三、GC垃圾回...
选择专区和圈子:{{title}}
{{o.name}}
{{m.name}}