SSD安装小记

刚开学就想败家, 想来想去还是把本子武装到牙齿吧, 于是入了SSD, Crucial M4 C400 64G, 无奈本子只有SATA Ⅱ上不了500M/s, 残念. 不过这篇文章的主题不是评测, 而是流水帐一下那蛋疼的安装过程.

原方案: HDD不动, SSD放光驱位, 引导放HDD. 因为引导本来就在一个单独的分区上, 看有人引导放SSD上引导不了, 索性引导不动.

因为U盘量产成了一个光驱一个U盘, 光驱模拟的是XP, 懒得动了, 于是考虑从硬盘装Win7. 把ISO解压到HDD, 重启F8选修复, 进去后选cmd, 熟练地切换到Win7目录(source), setup, 居然显示"无法启动找不到所需的子程序"!

Well, well, 难不倒我, 大不了换个办法引导进去开setup便是. 给拆下来的光驱插上电路板(一并买了个光驱盒, 做工奇烂, 就一塑料盒+电路板+2根线), 插上电脑随便找了张Win7 RC引导进去进cmd, 还是切换到HDD里的Win7目录setup, 这回成功了. 至于为啥不直接从光驱装Win7, 因为刻的盘都太老了, 这几年嫌从光盘装太慢都直接从硬盘开始装.

恩, 成功给SSD分完区格式化, 卧槽! 居然说"无法在此分区上安装", 无视之直接下一步, 卧槽无法安装居然可以安装了! 于是6min复制完文件重启.

重启后, 进不去装到一半的系统, 找不到所需驱动, 好吧, 果然"无法在此分区上安装"= =, 莫非是因为SSD放在了光驱位? 好吧好吧, 咱调换一下便是, 反正原来光驱面板拆不下来现在光驱位用普通面板好像相性不是很好的样子留了个小洞, 应该不用太担心散热= =.

调换后, 再次重装系统, 这次不提示不能装了, 恩, 这次复制完重启后压根连引导界面都看不见了直接提示找不到Device了! 靠, Acer这破BIOS果然引导时候是不给光驱位设备加电的! 于是HDD上的引导读不到了!

很好很好, 全部推倒重来吧. 为了避免转Win7的时候自动把引导加到HDD上去直接把HDD拔了开始装, 不过把HDD拔了如何开始装?! 好吧好吧, 还有移动硬盘, 用Win7 RC的光盘引导进PE, cd到移动硬盘里的新Win7开始装.

安装一切顺利, 装完关机后插上HDD, 进入系统后正常使用. 使用大半天后温度保持在33°C, 问题不大吧, 不知夏天如何.

至此系统安装完毕, 开机至进入桌面用时25s.

感想1: 以后买本子先看BIOS, 这Insyde的BIOS弱爆了啊毛都不能调啊喂= =.
感想2: Acer乃给螺丝打蜡也就算了你还给光驱面板扣子里塞胶水!!!

感想3: 拆机必备STANLEY><.

Advertisements

imageInstant – 图片即时搜索脚本

现在以图搜图的搜索引擎也不少了, 一般都支持通过URL查找或通过上传文件查找. 不过每次想找图时都要另开一页把图片地址复制进去颇为不便, 于是就做了这么一个脚本以Bookmarklet的形式调用, 在图片上生成一个菜单, 直接选择搜索引擎搜索即可.

imageInstant

其实这个脚本还是上个学期末写的, 寒假看了本《Javascript语言精粹》, 受益匪浅, 可谓字字精华没有一句废话, 于是这几天就把这个脚本重写了一遍, 感觉正如书中所说, 如果除去那些糟粕, Javascript确实是门非常优雅的语言.

项目托管在GitHub.

主页: http://archangelsdy.github.com/iqdbInstant/
源码: https://github.com/ArchangelSDY/iqdbInstant

32-bit BMP转PNG之C#版

.Net库中默认的Bitmap类是不支持读取32-bit BMP的, 注意是不支持直接读, 本身是可以处理ARGB结构的. 既然这样那就自己一个个字节读吧, 反正BMP又不需要解码一个一个像素挨个读取便是.

不过读取了每个像素的各个分量值后如果挨个setPixel()的效率是不可忍受的. 且不说双层循环本来效率就很差, setPixel()本来就不快. 还好微软自己也觉得这速度太不靠谱, 留下了用指针直接操作的方法.

主要代码如下:

using (FileStream fs = new FileStream(file, FileMode.Open))
{
    //判断像素深度, 仅对32-bit BMP操作
    fs.Position = 0x1C;
    byte[] byteDepth = new byte[4];
    fs.Read(byteDepth, 0, 4);
    int depth = BitConverter.ToInt32(byteDepth, 0);
    if (depth != 32)
    {
        Console.Write("Not 32-bit bmp file.n");
        continue;
    }
    else
    {
        DateTime dt = DateTime.Now;
    
        //Pixel数据区开始位置
        fs.Position = 0xA;
        byte[] bytePixelArrayStart = new byte[4];
        fs.Read(bytePixelArrayStart, 0, 4);
        int posPixelArrayStart = BitConverter.ToInt32(bytePixelArrayStart, 0);
    
        //图片宽度
        fs.Position = 0x12;
        byte[] byteWidth = new byte[4];
        fs.Read(byteWidth, 0, 4);
        int width = BitConverter.ToInt32(byteWidth, 0);
    
        //图片高度
        fs.Position = 0x16;
        byte[] byteHeight = new byte[4];
        fs.Read(byteHeight, 0, 4);
        int height = BitConverter.ToInt32(byteHeight, 0);

        fs.Position = posPixelArrayStart;
        //建立一个与原图等大的Bitmap对象, 设置像素格式为32-bitARGB
        using (Bitmap bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb))
        {
            //获取该Bitmap对象的BitmapData数据
            BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, bmp.PixelFormat);
            //获取数据开始的指针
            IntPtr ptr = bmpData.Scan0;
            //计算数据区的长度
            int imgLen = Math.Abs(bmpData.Stride) * bmp.Height;
            byte[] pixels = new byte[imgLen];
            
            //逐个Byte读取值并写入对应像素的各个通道
            for (int counter = 0; counter < pixels.Length; counter++)
            {
                byte[] color = new byte[1];
                fs.Read(color, 0, 1);
                pixels[counter] = color[0];
            }
            
            //直接将新的Pixel数据复制入BitmapData对象, 覆盖原有数据
            System.Runtime.InteropServices.Marshal.Copy(pixels, 0, ptr, imgLen);
            bmp.UnlockBits(bmpData);
    
            //这样生成的图时y方向上下颠倒的, 需要做一次翻转
            //这样的翻转不会导致图片质量的损失
            bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
    
            //转换成PNG格式并写入新文件
            string pngFile = string.Format(@"{0}{1}.png", Path.GetDirectoryName(file), Path.GetFileNameWithoutExtension(file));
            bmp.Save(pngFile, ImageFormat.Png);
    
            Console.Write("Used: {0}ms.n", (DateTime.Now - dt).TotalMilliseconds);
        }
    }
}

测试表明使用指针直接操作Pixel数据后速度有显著提升.

另有个小工具叫AlphaConv也可以实现32-bit BMP和PNG互转, Delphi写的, 调的libpng, 效率应该不错, 可惜只有图形界面.

有空来试试直接用C调libpng来实现一下, 看能比.Net快多少><.

Dnsmasq设置简介

说是简介其实也不想详细介绍, 具体官网上都有, 一搜便知. 这里只是简单地罗列下常用配置, 以供参考.

#不读取/etc/resolv.conf文件
no-resolv

#不扫描/etc/resolv.conf和/etc/dnsmasq.conf文件的改动,如果有改动直接重启程序即可
no-poll

#设置dns服务器
server=8.8.8.8

#指定*.domain.name用8.8.8.8解析
server=/domain.name/8.8.8.8
注意不是server=/*.domain.name/8.8.8.8

#解析*.domain.name到127.0.0.1
address=/domain.name/127.0.0.1

#无效的DNS服务器, 用于反DNS劫持
bogus-nxdomain=127.0.0.1

下列一个典型的配置文件, 用于tomato, 要使其生效请开启截获DNS端口.

#覆盖掉原有DNS设置
no-resolv

#对baidu.com及其子域名使用中国电信的DNS
server=/baidu.com/218.2.135.1

#对其它的域名使用Google DNS
server=8.8.8.8

C#控制台输出实时重定向

.Net Framework中的Process类和ProcessStartInfo类可以方便地控制进程的启动, 还可以方便地将控制台程序的输出重定向到Stream中, 然后显示到TextBox中或者记录到文件中. 不过要实时显示每一行的输出还得用点技巧, 方法如下.

启动进程前的准备.

//设置待启动程序的路径和要传递的参数
ProcessStartInfo info = new ProcessStartInfo(path, args));

//将标准输出流重定向, 需同时把UseShellExecute设置为false
info.RedirectStandardOutput = true;

//不使用操作系统外壳程序启动
info.UseShellExecute = false;

//不创建窗体
info.CreateNoWindow = true;

为了做到实时捕获, 需在进程结束前一直搜索输出流, 如有新的一行便读取并引发一个事件以让主程序窗体能够响应并将这行添加至TextBox.

if (process.Start())
{
    while (!process.HasExited)
    {
        string line = process.StandardOutput.ReadLine();
        if (line != null)
            OutputLineGet(line);
    }
    process.WaitForExit();
}

为了不让窗体假死, 将调用上述循环放至另一个的单独线程中.

public void Start()
{
    new System.Threading.Thread(new System.Threading.ThreadStart(this.StartConsoleProgram)).Start();
}


然后便是窗体响应该事件, 注意到是跨线程调用GUI, 需用委托.

public MainForm()
{
  InitializeComponent();

  //注册事件
  this.OutputLineGet += new OnOutputLineGetDelegate(MainForm_OutputLineGet);
}

public delegate void OnOutputLineGetDelegate(string line);
public event OnOutputLineGetDelegate OutputLineGet;

private void MainForm_OutputLineGet(string line)
{
    if (this.InvokeRequired)
    {
        this.Invoke(new OnOutputLineGetDelegate(MainForm_OutputLineGet), line);
    }
    else
    {
        txtLog.AppendText(line);

        //添加换行符
        txtLog.AppendText("rn");
    }
}

这样便能一行一行实时捕获输出了, 而且主程序也可以响应窗体消息不至于假死.

LilyBot

10.1期间写了LilyBot, 用来定时抓取教务网的最新通知和百合上特定板块的帖子, 然后生成RSS. 从此系版和教务网上的最新通知都可以统一抓到Google Reader中, 我等收不到飞信不用人人不逛BBS的人终于也可以与时俱进了><. 无奈10.1期间vps抽得厉害, 掉包率40%~80%, ping值能上4000ms, 根本没法ssh上去调试, 于是拖到了现在. 目前抓取目标如下:

1. 教务网通知;
2. 百合DII版;
3. 百合Pictures版;
4. 百合M_Academic版.

百合限制了单位时间内同一ip的连接数, 抓帖子内容比较慢, 这些抓一遍大概2min(其实应该能更快, 保险起见设置了抓一贴sleep(1)), 所以暂时就先抓这么多, 谁有需求我再加.

Firefox可以直接把RSS订阅到书签栏. Chrome有RSS Live Links扩展. IE也可以直接订阅, 而且还能同时在桌面小工具里显示.

教务网通知只抓了标题, 啥时有空把内容也抓了好了><~

jQuery实现滑动效果

最近网上似乎开始流行滑动效果, 比如说M$主页上的这个. 虽然M$的这个是为了配合Metro的主题, 但是滑动展示却确实广泛应用于各大网站的主页上. 网上实现滑动效果的js插件一抓一大把, 但是既然我们用了jQuery做Ajax, 何不想想jQuery框架内就能解决的方案.

解决大体思路是: 在一个wrapper div里放N个等大的sub div, 并列放置, 多余部分不显示, 滑动时改变各个sub div的水平位置即可实现slide. 当然为了做成"滑动", 要用到jQuery的.animate()方法(当然别的框架也可以).

并列放置一般来说是把每个sub div的float设成left, 不过float其实并不是并列排版的最佳方案, 因为sub div从wrapper中"浮"了出来不再属于wrapper, 造成wrapper包不住sub. 需在某处应用clear, 典型的方法是新建个空div应用clear: both. 这样做未免有点Hack的味道, 空的div也不容易理解.

另一个方法是将每个sub div的position设成relative, 然后将后一个的top设成-300px(如每个sub div高度为300px). 这里的top是相对于前一个div而言的, 所以后面第二个的top应设为-600px. 关于relative的排版参见这里.

CSS3中对于元素并列放置有更好的解决方案: display: inline-block. 允许像div这种块级元素拥有inline的特性. 强烈建议对于块级元素的并列排放使用inline-block而不是float.

我用了第二种方法(虽然事实上第三种方法更好), 不过怎么排版其实并不重要, 排成一排就行了.

对于wrapper, 用overflow: hidden隐藏多余的sub div.

向左滑动时, 在js中对所有sub div调用.animate({"left", "-=300px"}).
向右滑动时, 在js中对所有sub div调用.animate({"left", "+=300px"}).

未防滑过头, 滑动之前需判断是否已到底.

其它方法可能调用.animate()时渐变的属性略有不同, 相应调整即可. 不过.animate()可调的属性限于几个可以连续变化的数值属性, 具体可参阅jQuery的文档.

简言之, 选择一个合适的方法并列排版sub div, 遮掉多余部分, 滑动时左右调整位置, 就这么简单.

附: Source Code