Bmp24Writer代码

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 条评论
登录 后参与评论

相关文章

来自专栏函数式编程语言及工具

Scalaz(18)- Monad: ReaderWriterState-可以是一种简单的编程语言

  说道FP,我们马上会联想到Monad。我们说过Monad的代表函数flatMap可以把两个运算F[A],F[B]连续起来,这样就可以从程序的意义上形成一种串...

20670
来自专栏指尖下的Android

二进制的运算

在计算机中存储字节是定长的,即我们8、16、32位等等,6的二进制位为110,但如果在8位计算机中是00000110,高位补零

26730
来自专栏一个会写诗的程序员的博客

Java 8中的Optional 类型与 Kotlin 中的可空类型Java 8中的Optional 类型与 Kotlin 中的可空类型Kotlin 中的可空类型《Kotlin极简教程》正式上架:

其中,我们使用 String? 同样表达了 Optional<String>的意思,相比之下,哪个更简单?

9910
来自专栏一个会写诗的程序员的博客

《Kotlin极简教程》第四章 Kotlin基础语法表达式Null Check循环枚举遍历Map拼接字符串基本类型

在Kotlin中,所有东西都是对象,所以我们可以调用成员函数和属性的任何变量对象。有些类型是内置的,他们的实现被优化过, 但是用户看起来他们就像普通的类. 本节...

22830
来自专栏一个会写诗的程序员的博客

Kotlin 语言极简教程 v0.1 (东海陈光剑)Kotlin 语言极简教程

在 Java 里,声明一个 string 类型,赋一个 null 给这个变量。一旦我们要打印这个字符串的时候,会在运行时曝出空指针错误,因为我们在尝试去读一个空...

10740
来自专栏Golang语言社区

Goalng下的反射模块reflect学习使用

注意:我们上面的示例是使用值类型进行进行反射构造的。如果是指针类型的话,我们需要使用reflect.Struct字段进行判断接口类型的Kind()方法

19130
来自专栏Java编程

Java习惯用法总结

在Java编程中,有些知识 并不能仅通过语言规范或者标准API文档就能学到的。在本文中,我会尽量收集一些最常用的习惯用法,特别是很难猜到的用法。(Joshua ...

1.1K30
来自专栏技术小黑屋

为什么局部变量需要显式设置初始化值

我们在编程中,无时无刻地都在于方法打交道,而在方法中,我们很难不使用局部变量,比如我们有下面的这样一段很简单的代码

17640
来自专栏Python爱好者

Java基础笔记22

15220
来自专栏Java 技术分享

Java 基础 -- 泛型、集合、IO、反射

38890

扫码关注云+社区

领取腾讯云代金券