您好!欢迎来到爱源码

爱源码

热门搜索: 抖音快手短视频下载   

【iOS】位图位图详细讲解和练习 《互站网》

  • 时间:2022-08-25 01:12 编辑: 来源: 阅读:288
  • 扫一扫,手机访问
摘要:【iOS】位图位图详细讲解和练习 《互站网》
在工作中,我们经常会遇到UIImage的各种解决方案,比如旋转、放大缩小、裁剪等。,进一步,我们可以操作图片上的像素。 最近对位图有了更多的了解。 下面这篇文章主要分为以下几个部分:了解BitmapiOS中的位图代码练习了解位图位图,又称光栅图形或位图,是一种用像素阵列(Pixel-array/Dot-matrix)表示的图像。 位图的每个像素都被分配了一个特定的位置和颜色值。 每个像素的颜色信息用RGB组合或灰度值来表示。 根据位深度,位图可以分为1、4、8、16、24和32位(https://baike.baidu.com/item/%E4%BD%8D)图像。 每个像素使用的信息量越多,可用的颜色就越多,颜色就越鲜艳,对应的数据量就越大。 例如,位深度为1的像素位图只有两种可能的值(黑色和白色),因此也称为二进制位图。 位深度为8的图像有2个(即256个)可能值。 在深度为8位的灰度模式图像中有256个可能的灰度值。 RGB图像由三个颜色通道组成。 8位/通道RGB图像中的每个通道有256个可能的值,这意味着图像有超过1600万个可能的颜色值。 有时,每通道8位(bpc)的RGB图像被称为24位图像(每像素8位x 3通道= 24位数据) 通常用24位RGB组合数据位表示的位图称为真彩色位图。 摘自百度百科 从上面的描述中,我们可以把位图理解为位图或者数组,其中每个元素都是一个像素信息。假设对于32位RGBA图像,每个元素包含三个颜色分量(R、G、B)和一个阿尔法分量,每个分量占用8位(8位= 1字节= 32/4)。 这些像素一起可以代表一幅图片。 iOS中的位图在iOS中,位图的数据是用CGImageRef封装的。 CGImageRef对象cgimagecreate可以通过以下函数创建——这是最灵活的方法,但也是最复杂的方法,需要传入11个参数。这个方法会在最后解释。 CGImageSourceCreate-Imageatindex-Create CGImageSourceCreate-Thumbnailatindex通过一个已有的Image对象-类似于前面的函数,这只是创建缩略图CGBitMapContextCreateImage-创建CGImageCreateWith-ImageInrect通过复制位图图形-创建某个矩形中的数据。如果想用位图多种方式求解图片,需要先创建一个位图上下文。 (CGBitmapContextCreate,或者Swift中的CGContext)先看一下初始化方法:cgcontextref _ _ nullable CGBitmapContextCreate(void * _ _ nullable data,size_t width,size_t height,size_t bitsPerComponent,size_t bytesPerRow,CGColorSpaceRef space,Uint32_t bitmapInfo)下面是一个例子:让w = int (image.size.width)让h = int (image.size.height)让bitsPerComponent = 8让bytesPerRow = wUInt32 & gt。allocate(capacity:w * h)buffer data . initialize(repeating:0,count:w * h)let cxt = CG context(data:buffer data,width: w,height: h,bitsPerComponent:bitsPerComponent,bytesPerRow: bytesPerRow,space: colorSpace,BitmapInfo: bitmapInfo)参数描述数据:用于存储位图的点阵数据。当生成上下文,通过调用CGContextDrawImage方法将指定的图片绘制到上下文中时,图片的位图像素信息就会在数据中,可以作为数组指针使用。 我们可以对这些数据的内容进行操作,然后使用这些数据作为主要参数,通过生成CGDataProvider的实例并调用CGImageCreate方法来重新生成一个CGImage。 宽度和高度:位图的宽度和高度。 例如,宽度= 10,高度= 20意味着每行10个像素,每列20个像素。 BitsPerComponent:颜色分量或alpha分量的位数。 以32位图像为例:bitsPerComponent = 8bytesPerRow:位图每行占用的字节数。 以32位图像为例:如果一个像素有4个字节(RGBA),那么bytesPerRow = width * 4space:颜色空间,是RGBA、CMYK还是灰度值? rgba:cgcolorspace createdevicegb()cmyk:cgcolorspace createdevicecmyk()灰度值:cgcolorspace createdevicegray()BitMapInfo:一个常量,描述该位图上下文对应的位图的基本信息。 通常是多个枚举值的OR运算的最终值(CGBitmapInfo和CGImageAlphaInfo)。 比如顶部是否可以有alpha通道,alpha通道的位置(RGBA还是ARGB),字节排列的顺序等等。 Bitmapinfo和cgimagealphainputblicstruct CGBitmapInfo:options et { public init(raw value:uint 32)public static var alpha info mask:CGBitmapInfo { get } public static var float mask:CGBitmapInfo { get } public static var byteardermask:CGBitmapInfo { get } public static var byte order 16 little:CGBitmapInfo { get } public static var byte order 32 little:CGBitmapInfo { get } public static*/ case premultipliedLast /*例如,预乘RGBA */ case premultipliedFirst /*例如,预乘ARGB */ case last /*例如,非预乘RGBA */ case first /*例如,非预乘ARGB */ case noneSkipLast /*例如,RBGX。*/CASE NONESKIPFIRST/*例如XRGB。*/仅大小写ALPHA/*无颜色数据,仅ALPHA数据*/}以上是CGBitmapInfo和CGImageAlphaInfo的定义。 有几个关键点需要说明:。最后和第一个:...last表示alpha分量在末尾,即RGBA。 然后按以下顺序解析颜色和alpha分量:让r = CG float((pixel & gt:& gt;0)& amp;0xff) / 255.0让g = CG float((pixel & gt;& gt8)& amp;0xff) / 255.0让b = CG float((pixel & gt;& gt16)& amp;0xff) / 255.0让a = CG float((pixel & gt;& gt24)& amp;0x ff)/255.0 let color = ui color(display p3red:r,green: g,blue: b,alpha: 1)...第一个意味着阿尔法组件在开始,即ARGB。 然后按以下顺序解析颜色和alpha分量:让a = CG float((pixel & gt:& gt;0)& amp;0xff) / 255.0设r = CG float((pixel & gt;& gt8)& amp;0xff) / 255.0让g = CG float((pixel & gt;& gt16)& amp;0xff) / 255.0让b = CG float((pixel & gt;& gt24)& amp;0x)/255.0 let color = ui color(display p3red:r,green: g,blue: b,alpha: 1)预乘透明度:例如常规半透明图像的RGBA的归一化值为(1,0.5,0.5,0.5),如果预乘透明度,RGBA的归一化值为(1 * 0.5,0.5 * 0.5,0.5 * 0.5)=(1,0.25,0.25,0.5),即每 Color.rgb *= color.alpha因此,在对图像进行预乘时,意味着在对图像进行解码和压缩时,将alpha通道的值分别乘以颜色分量,我们知道alpha会影响颜色的透明度。如果我们在压缩的时候完成了这一步,那么渲染的时候就不用解alpha通道了,只需要直接显示位图就可以了,提高了性能。 因此,如果将bitmapInfo指定为premultipliedFirst或premultipliedLast,则在生成位图上下文后,解析的rgb颜色值是乘以alpha的值。 那么如果我不想乘以透明度,只是想得到原来的rgb颜色值呢?直接将bitmapInfo指定为last或first可以吗?如下:let w = 1 let h = 1 let bitspercomplete = 8 let bytes perow = w * 4 let color space = cgcolorspacecreatedevicegb()let bitmapInfo = cgimagealphainfo . last . raw value var buffer data = Array & lt;UInt32 & gt(重复:0,计数:1)guard let cxt = CG context(data:& amp;bufferData,width: w,height: h,bitsper component:bitsper component,bytesPerRow: bytesPerRow,space: colorSpace,Bitmapinfo:Bitmapinfo)else { return nil }此时我们会发现生成的位图上下文cxt为nil,控制台输出以下错误消息:bubblepanimationdemo[24851:2078573][未知进程名] CGMAP ContextCreate:不支持的参数组合:Set CGBitmap _ Context _ Log _ Errors环境变量查看此处的详细信息非石膏或。noneSkipFirst代替。最后还是。首先。 “noneSkip”表示有alpha分量,忽略该值表示透明度没有影响。 因此,如果bitmapInfo被指定为。非SkipFirst or。noneSkipLast,不会有例外,我们终于可以在不预乘alpha的情况下解析原始rgb颜色值了。 CGBitMapInfo CGBitMapInfo是一个枚举几何体,用于描述位图的基本信息。 //获取图片的位图信息let bitmapInfo = image。CGImage.bitmapinfo当我们创建位图上下文或者CGImage指定bitmapinfo时,如果要使用CGBitmapInfo,我们一般会对其值和CGImageAlphaInfo做按位或运算。 如下:cgimagealphainfo。premultiplied last . raw value | CGBitmapInfo . byte order 32 big . raw value以下是Apple的帮助文档中cgbitmapinfo的摘要。 使用ARGB格式在内存中存储像素数据的应用程序必须注意如何读取数据。如果代码写得不正确,就有可能误读数据,导致颜色或alpha出现错误。字节顺序常数指定像素格式的字节顺序,若要指定字节顺序,请使用按位or运算符将适当的常数与bitmapinfo参数组合起来。这可能意味着存储在内存中的ARGB格式的像素数据必须注意读取这些数据的方式。 如果没有读对,会造成颜色和透明度显示错误。 字节顺序常数标识该像素中每个分量的字节排列。 如果要指示字节排列,需要通过按位OR来组合bitmapInfo中的值。 解释属性alphaInfoMask用于标识位图是否可以有alpha通道floatComponents。位图中每个分量的值是否可以是浮点值byteOrderMask像素字节排序格式byteOrder16Little16位图像将小端的字节分量排列到小端在其侧的32位图像。排列字节成分byteOrder16Big16位图像排列字节成分byteOrder32Big32位图像排列字节成分floatInfoMask用大端。没有任何解释。下面的例子是输出上面一张图片的位图信息:-(void)image dump:(ns string *)file { ui image * image =[ui image named:file];CGImageRef cgimage = image。CGImagesize _ t width = CGImageGetWidth(cgimage);size _ t height = CGImageGetHeight(cgimage);size _ t BPR = CGImageGetBytesPerRow(CG image);size _ t bpp = CGImageGetBitsPerPixel(cgimage);size _ t BPC = CGImageGetBitsPerComponent(CG image);size _ t bytes _ per _ pixel = bpp/BPC;CGBitmapInfo info = CGImageGetBitmapInfo(cgimage);NSLog(@ " \ n " " = = = = = = % @ = = = = \ n " " CGImageGetHeight:% d \ n " " CGImageGetWidth:% d \ n " " CGImageGetBitsPerPixel:% d \ n " " CGImageGetBitsPerComponent:% d \ n " " CGImageGetBytesPerRow:% d \ n " " CGImageGetBitmapInfo:0x % . 8x \ n " " kCGBitmapAlphaInfoMask = % s \ n " " kCGBitmapFloatComponents = % s \ n " " kcgbitmapbyteordermdermkCGBitmapAlphaInfoMask)?“是”:“否”,(信息& ampkCGBitmapFloatComponents)?“是”:“否”,(信息& ampkCGBitmapByteOrderMask)?“是”:“否”,(信息& ampkCGBitmapByteOrderDefault)?“是”:“否”,(信息& ampkCGBitmapByteOrder16Little)?“是”:“否”,(信息& ampkCGBitmapByteOrder32Little)?“是”:“否”,(信息& ampkCGBitmapByteOrder16Big)?“是”:“否”,(信息& ampkCGBitmapByteOrder32Big)?“是”:“否”);CGDataProviderRef provider = cgimagegetdata provider(CG image);ns data * data =(id)CGDataProviderCopyData(provider);[数据自动发布];const uint 8 _ t * bytes =[数据字节];printf("像素数据:\ n ");for(size _ t row = 0;row & lt身高;row++){ for(size _ t col = 0;col & lt宽度;col++){ const uint 8 _ t * pixel = & amp;bytes[row * BPR+col * bytes _ per _ pixel];printf("(");for(size _ t x = 0;x & lt每像素字节数;x++) { printf("%.2X ",pixel[x]);if(x & lt;bytes_per_pixel - 1 ) printf(",");} printf(")";if(col & lt;width - 1 ) printf(",");} printf(" \ n ");}}下面是byteOrder和alphaInfo中last和first的搭配。 ByteOrderXXLittle:生成的信息位置逆序byteOrderXXBig:生成的信息位置逆序XXXXFirst: ARGBXXXXLast: RGBA属性结果。premalliedfirst+. byte order 32 bigargb . premallie . D+。字节序32。pre multiplied first+。字节序32小GBA。pre multiplied list+。ByteOrder 32 Littlea R GBB,两者都是通过按位或运算得到最终值。 修行终于来修行了。看了几位大神的博客,根据他们的代码补充了一些想法和解释。 如果想看原帖,可以跳到最后参考部分。 获取图片中点击位置的颜色:获取imageView控件边界内的像素数据。 通过CG点的参数计算对应于CG点的像素的索引位置,并取出像素数据。 逐字节分析rgb颜色分量和alpha分量值,最后生成UIColor,最后返回结果。 扩展ui imageview { func color(for point p:CG point)-& gt;UIColor?{ guard let pixels = self . pixels Else { return nil } guard let index = pixel index(for:p)Else { return nil } let color = self . color(for pixel:pixels[index])return color }/*获取图片的像素数据*/ var pixels: [UInt32]?{ return self . getpixelsdata(in rect:self . bounds)}/*根据坐标点获取该点对应的像素在数组中的索引-p:顶部坐标点*/func pixel index(for p:CG point)->;Int?{ let size = self . bounds . size guard p . x & gt;0 & amp& ampp.x & lt=尺寸、宽度和尺寸。& ampp.y & gt0 & amp& ampp.y & ltSize.height else {return nil} //相当于height * bytes perow+x let float index = int(size . width * p . y+p . x)let Int index = Int(size . width)* Int(p . y)+Int(p . x)print(" float index:\(float index),intIndex : \ (IntIndex)") //这里必须将所有的索引转换成Int类型,然后进行求值,否则最终计算出来的索引会有偏差Return Int(size . width)* Int(p . y)+Int(pcolor {//创建位图上下文时,可以指定两种bitmapInfo //如果指定了premultipliedFirst,说明颜色分量按照alpha红绿蓝的顺序排列。//如果指定了premultipliedLast,则表示颜色分量按照红绿蓝alpha的顺序排列。//那么下面R,G,B,A四个值就按不同的顺序来分析了。 设r = CG float((pixel & gt;& gt0)& amp;0xff) / 255.0让g = CG float((pixel & gt;& gt8)& amp;0xff) / 255.0让b = CG float((pixel & gt;& gt16)& amp;0xff) / 255.0让a = CG float((pixel & gt;& gt24)& amp;0xff) / 255.0 print("r : \(r),g : \(g),b : \(b),a:\(a)")let color = ui color(display p3red:r,green: g,blue: b,Alpha: 1) return color} /*获取图片中指定范围内的位图数据(rgba数组)-rect:设置要获取的像素数组的范围以生成rect范围内的像素数据,比较耗时,实际使用时最好有缓存策略。 */func getPixelsData(in rect rect:CG rect)-& gt;[UInt32]?{guard let img = self.image,let cgimg = img . cgimageelse { return nil }/*不能直接用图像的宽度和高度作为绘图的宽度和高度,因为图像的大小可能远大于控件的大小。 所以在生成bitmapContext时,实际控件宽度和高度要占优势*/let w = int(rect . size . width)let h = int(rect . size . height)Bitspercomponent = 8//32位图像,所以如果每个颜色分量包含8位LetterBytesperow = w * 4//1字节= 8位图像,每个像素包含4个bytelet color spaces = cgcolorspacecreatedevicegb()let bitmapinfo = cgimagealphainfo.premaultiledfast.rawvalue// RGBA//let bitmapinfo = cgimagealphainfo.premultipliedfirst.rawvalue//argb//As它是32位图像,RGBA占用8位 var bufferData = Array & ltUInt32 & gt(重复:0,计数:w * h)guard let cxt = CG context(data:& amp;bufferData,width: w,height: h,bitsper component:bitsper component,bytesPerRow: bytesPerRow,space: colorSpace,Bitmapinfo:Bitmapinfo)else { return nil }//将图像绘制到上下文CXT中。Draw (cgimg,in: rect)返回缓冲区数据}}【注1】第一步获取图像的像素数据(getPixelsData)是一个比较耗时的操作。 在实际使用中,应该在采集后进行缓存,以便以后重用。 【注2】计算代码中指定像素的索引位置时,所有计算元素在计算前都要转换成Int类型。如果用CGFloat类型计算,然后转换成int类型,得到的索引值会有偏差,导致颜色不正确。 int(size . width * p . y+p . x)-& gt;error int(size . width)* int(p . y)+int(p . x)->:另一种获取图片中点击位置颜色的正确方法是生成一个只保存一个像素的BitmapContex。 根据p点的位置平移BitmapContext,使BitmapContext的绘图原点位于p点。 (默认渲染原点在左上角)/*另一个思路是获取点击位置的颜色。 上面的GetPixelsData需要得到整张图片的像素数据,对于只想得到某个点位置的颜色来说效率很低。 所以只生成包含一个像素的位图,然后直接根据bufferData中的像素数据生成颜色并返回*/func getcolor(from point p:CG point)->:ui color?{ let w = 1 let h = 1 let bitsPerComponent = 8 let bytes perrow = w * 4 let colorSpace = cgcolorspace createdevicergb()let bitmapInfo = cgbitmapinfo . byte order 32 big . raw value | Imagealphainfo.noneskinfast.rawvalue//rgba//can被公布为具有1个元素的UInt32数组var bufferdata = array < UInt32 & gt(repeating: 0,count: 1) //或具有4个元素的UInt8数组//var buffer data = array < uint 8 & gt;(重复:0,计数:4)guard let cxt = CG context(data:& amp;bufferData,width: w,height: h,bitsper component:bitsper component,bytesPerRow: bytesPerRow,space: colorSpace,Bitmapinfo:Bitmapinfo)else { return nil }/*这里注意,由于上面生成的位图上下文只包含一个像素数据,相当于一个点。 这个位图上下文的默认渲染原点是图片的左上角,也就是(0,0)的位置。如果直接从bufferData中获取,其实就是图片左上角第一个像素的颜色。 所以需要反方向平移位图上下文,使点P成为位图上下文的渲染原点*/cxt.translate by (x:-p.x,y:-p.y)/*将图像渲染到上下文中。这里需要注意的是,需要翻译后渲染,否则得到的颜色是不正确的。 */ layer.render(in: cxt) //只包含一个UInt32像素数据let component = bufferData.first!设r = CG float((component & gt;& gt0)& amp;0xff) / 255.0设g = CG float((component & gt;& gt8)& amp;0xff) / 255.0让b = CG float((component & gt;& gt16)& amp;0xff) / 255.0设a = CG float((component & gt;& gt24)& amp;0x)/255.0//包含四个UInt8(每个元素代表一个RGBA)元素的数组//let r = CG float(buffer data[0])/255.0//let g = CG float(buffer data[1])/255.0//let b = CG float(buffer data[2])/255.0//let a = CG float(buffer data[3])/25.0 let color = ui color(display P3 red:r,green: g,blue: b,alpha: a 生成一个小位图上下文,统计这个上下文的像素数据中出现次数最多的rgba,并返回(getMaxCountColor方法)遍历1中生成的每个像素。如果rgb三个颜色值的偏差值小于我们指定的偏差值(leeway),就把这个像素的颜色改成我们想要指定的rgb颜色。 颜色修改后的像素数据(bufferData)作为参数生成CGDataProvider实例,然后使用CGDataProvider实例作为参数生成CGImage实例。 最后,由** CGImage**实例生成并返回一个修改了像素颜色的UIImage实例。 /*将图像中出现频率最高的颜色改为顶部颜色*/funcChangeMaxCountColorToColor(用red red: int,green: int,blue : Int,alpha : CGFloat,leeway : Float,sourceImage : UIImage?)-& gt;UIImage?{ guard let image = sourceImage,let CG image = image . CG image else { return nil } let w = Int(image . size . width)let h = Int(image . size . height)let bitsPerComponent = 8 let bytes perrow = w * 4 let colorSpace = cgcolorspace createdevicergb()let bitmapInfo = cgimagealphainfo . premultiplielast . raw value | cgbitmapinfo . byte order 32 big . raw value//var buffer data = Array & lt;UInt32 & gt(repeating: 0,count:w * h)//var buffer data =[uint 32](repeat element(0,count:w * h))let buffer data = UnsafeMutablePointer & lt;UInt8 & gt。allocate(capacity:w * h * 4)buffer data . initialize(repeating:0,count:w * h)guard let cxt = CG context(data:buffer data,width: w,height: h,bitsPerComponent:bitsPerComponent,bytesPerRow: bytesPerRow,space: colorSpace,bitmapInfo:bitmapInfo)else { return nil } cxt . draw(CG image,in: CGRect(x: 0,y: 0,width: CGFloat(w),height:CG float(h))guard let maxC =..& ltw * h { let byteStart = I * 4 let r = Float(buffer data . advanced(by:byteStart))。指针对象)设g = Float(buffer data . advanced(by:byteStart+1)。指针对象)设b = Float(buffer data . advanced(by:byteStart+2)。指针对象)如果ABS(Float(maxc . r)-r)& lt;回旋余地& amp& ampABS(Float(maxc . g)-g)& lt;回旋余地& amp& ampABS(Float(maxc . b)-b)& lt;leyway { buffer data . advanced(by:byteStart)。pointee = uint 8(red)buffer data . advanced(by:byteStart+1)。pointee = UInt8(绿色)缓冲区数据. advanced (by: bytestart+2)。pointee = uint 8(blue)//传入的alpha是规格化值,所以需要除以255.0 BufferData。高级(by: bytestart+3)。pointee = uint 8(alpha * 255.0)} } let data provider = cgdata provider(dataInfo:nil,data: bufferData,size: bytesPerRow * h) { (_,data,_)in data . deallocate()} guard let provider = data provider else { return nil } let cgBitmapInfo 32 = cgimagealpha info . premultipliedlast . raw value | cgBitmapInfo . byte order 32 big . raw value let cgBitmapInfo = cgBitmapInfo(raw value:cgBitmapInfo intent:cgcolorenderingintent . defaultintent)guard let new cgImage = newCGImageOptional else { return nil } let new image = ui image(cgImage:newCGImage)//如果在这里释放bufferData的内存,那么会导致新的图像被赋给imageView.image,图像看不到。 //应该在创建CGDataProvider时在回调函数中释放。//buffer data . de initialize(count:w * h)//buffer data . deallocate()Return new image }/*获取最频繁的rgba */funcgetmaxcountcolor(_ image:ui image?)-& gt;(r : Int,g : Int,b : Int,a : Int,pixelColor : UInt32)?{ guard let image = image,let CG image = image . CG image else { return nil }//先缩小图片加快计算速度。但结果越小,误差可能越大。设w = 150设h = 150设bitspercomplete = 8设bytesperow = w * 4设colorSpace = cgcolorspace createdevicergb()设bitmapInfo = cgimagealphainfo . premultiplied last . raw value//RGBA var buffer data = Array & lt;UInt32 & gt(重复:0,计数:w * h)guard let cxt = CG context(data:& amp;bufferData,width: w,height: h,bitsper component:bitsper component,bytesPerRow: bytesPerRow,space: colorSpace,bitmapInfo:bitmapInfo)else { return nil } cxt . draw(CG image,in: CGRect(x: 0,y: 0,width: CGFloat(w),height:CG float(h))var colorCountDic =[uint 32:Int]()var maxCountColor:uint 32 = 0 let color num = w * h for I in 0..& ltcolorNum { let color = buffer data[I]if let count = colorCountDic[color]{ colorCountDic[color]= count+1 } else { colorCountDic[color]= 1 } if let maxColorCount = colorCountDic[maxCountColor]{ if colorCountDic[color]!& gtmaxColorCount { maxCountColor = color } } else { maxCountColor = color } } let r = Int((maxCountColor & gt;& gt0)& amp;0xff)设g = Int((maxCountColor & gt;& gt8)& amp;0xff)设b = Int((maxCountColor & gt;& gt16)& amp;0xff)设a = Int((maxCountColor & gt;& gt24)& amp;0x) return (r,g,b,a,maxcountcolor)}/*将图像中出现频率最高的颜色改为透明色*/funcchangemaxcountcolor transparent(leeway:float,image: uiimage?)-& gt;UIImage?{ Return ChangeMaxCountColorColor(with red:0,green: 0,blue: 0,alpha: 0,leeway: 10,source image: image)}【注1】bufferData的生成方法与上面的例子不同。 在上面的getPixelsData方法中,bufferData是一个Swift数组,可以在从一个图像中获取像素数据后直接使用。 但是在这个例子中,bufferData的类型是UnsafeMutablePointer。 原因是如果bufferData是一个Swift数组,那么在生成CGDataProvider实例并最终生成UIImage之后,它不会显示在控件上。 UnsafeMutablePointer & ltUInt32 & gtType相当于C语言中的数组指针,也就是C语言中的数组。 如果你想使用这种类型的变量,你需要自己创建和分配内存并释放它。 具体可以参考一下:关于https://blog.csdn.net/zkh90644/article/details/52819002还有一点需要注意的是,如果使用UnsafeMutablePointer变量,不需要写地址符号(& BufferData),如果是普通的Swift数组,需要加【注2】。在这里,我使用UInt8数组来存储像素(上例中的UInt32数组)。原因是为了方便像素数据中颜色分量的重新赋值,而不是做各种按位AND和or运算。 对于0中的I..& ltw * h { let byteStart = I * 4 let r = Float(buffer data . advanced(by:byteStart))。指针对象)设g = Float(buffer data . advanced(by:byteStart+1)。指针对象)设b = Float(buffer data . advanced(by:byteStart+2)。指针对象)如果ABS(Float(maxc . r)-r)& lt;回旋余地& amp& ampABS(Float(maxc . g)-g)& lt;回旋余地& amp& ampABS(Float(maxc . b)-b)& lt;leyway { buffer data . advanced(by:byteStart)。pointee = uint 8(red)buffer data . advanced(by:byteStart+1)。pointee = UInt8(绿色)缓冲区数据. advanced (by: bytestart+2)。pointee = uint 8(blue)//传入的alpha是规格化值,因此,需要除以255.0 BufferData。高级(by: bytestart+3)。pointee = uint 8(alpha * 255.0)}[注3]修改颜色和基于像素数据生成CGImage时也要注明bitmapInfo。由于最终的图像可以是半透明的,我们需要alpha通道来生效。 所以要注明CGImageAlphaInfo是。premultipliedLast或。premultipliedFirst。 设置图片透明度func set alpha (_ alpha: cgfloat,sourceimage: uiimage?)-& gt;UIImage?{ guard let img = sourceImage,let cgImg = img . cgimage else { return nil } UIGraphicsBeginImageContextWithOptions(img . size,false,1)guardletcxt = uigraphicgetcurrentcontext()else { return nil }//调用draw方法后,图片的下镜像,所以这里需要做反转和平移变换cxt。按(x: 1,y:-1) CXT缩放。Translate by (x: 0,y:-img . size . height)cxt . setblendmode(cgblendmode . multiply)cxt . set alpha(alpha)cxt . draw(cgImg,in: CGRect(x: 0,y: 0,width: img.size.width,height:img . size . height))let new image = UIGraphicsGetImageFromCurrentImageContext()UIGraphicsEndImageContext()return new image }二进制压缩图片大小:static func compress 2 Data(_ comresimage:ui image= 0中_的零..& lt6 { compression =(max+min)/2 compressed data = comress image . JPEG data(compression quality:compression)!if CG float(compressed data . count)& lt;CG float(maxBytesLength)* 0.9 { min = compression } else if compressed data . count & gt;maxBytesLength { max = compression } else { break } } return compressed Data }获取图片格式:public enum image format type { case jpg case PNG case GIF case webp case unknown } public struct image header Data { static var PNG:[uint 8]=[0x 89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A]static var JPEG _ SOI:[uint 8]=[0x ff,0x D8]static var JPEG _ IF:[uint 8]=[itgetBytes(& amp;buffer,长度:8)if buffer = = ImageHeaderData.PNG { return。png } else if buffer[0]= = image header data。JPEG _ SOI[0]& amp;& ampbuffer[1] == ImageHeaderData。JPEG _ SOI[1]& amp;& ampbuffer[2] == ImageHeaderData。JPEG_IF[0] { return。jpg } else if buffer[0]= = image header data。GIF[0]& amp;& ampbuffer[1] == ImageHeaderData。GIF[1]& amp;& ampbuffer[2] == ImageHeaderData。GIF[2] { return。gif }如果计数& lt12 {返回。未知} let endIndex = index(startIndex,offset by:12)let testData = sub data(in:startIndex..& ltend index)guard let test String = String(data:test data,encoding:。ascii) else { return。未知} if test string . has prefix(" RIFF ")& amp;& ampteststring . hassuffix(" webp "){ return . webp } else { return . unknown } }这是我最近总结的关于图片和位图的内容。期间查阅了很多朋友的博客,学到了很多东西,也给了我很多思路。谢谢您们。 以后多学点相关知识,还会继续升级 参考:https://blog.csdn.net/rpf2014/article/details/52598280 https://www.jianshu.com/p/12d0ec666959 https://cloud.tencent.com/developer/ask/127227 https blog . csdn . net/hello _ hwc/article/details/49614263 https://blog . csdn . net/jeffasd/article/details/80571366 https://www . jinshu . com/p/52 e 6 fec1b 418 https://blog . csdn . net/jeffasd/article/details/78142067


  • 全部评论(0)
资讯详情页最新发布上方横幅
最新发布的资讯信息
【技术支持|常见问题】1556原创ng8文章搜索页面不齐(2024-05-01 14:43)
【技术支持|常见问题】1502企业站群-多域名跳转-多模板切换(2024-04-09 12:19)
【技术支持|常见问题】1126完美滑屏版视频只能显示10个(2024-03-29 13:37)
【技术支持|常见问题】响应式自适应代码(2024-03-24 14:23)
【技术支持|常见问题】1126完美滑屏版百度未授权使用地图api怎么办(2024-03-15 07:21)
【技术支持|常见问题】如何集成阿里通信短信接口(2024-02-19 21:48)
【技术支持|常见问题】算命网微信支付宝产品名称年份在哪修改?风水姻缘合婚配对_公司起名占卜八字算命算财运查吉凶源码(2024-01-07 12:27)
【域名/主机/服务器|】帝国CMS安装(2023-08-20 11:31)
【技术支持|常见问题】通过HTTPs测试Mozilla DNS {免费源码}(2022-11-04 10:37)
【技术支持|常见问题】别告诉我你没看过邰方这两则有思想的创意广告! (2022-11-04 10:37)

联系我们
Q Q:375457086
Q Q:526665408
电话:0755-84666665
微信:15999668636
联系客服
企业客服1 企业客服2 联系客服
86-755-84666665
手机版
手机版
扫一扫进手机版
返回顶部