레이블이 컴퓨터 일반인 게시물을 표시합니다. 모든 게시물 표시
레이블이 컴퓨터 일반인 게시물을 표시합니다. 모든 게시물 표시

2010년 12월 5일 일요일

TS/MTS → MKV 한방에 변환하기

많이 보던 인터페이스. VCi를 최대한 재활용했음.


파일 형식 중에 .ts(또는 .mts)라는 형식이 있다. (Transport Stream)
잡음이 많은 환경을 고려해서 설계된 컨테이너 포맷이다.

온라인 방송에서도 널리 쓰이고, DSLR에서도 가끔 쓰이는 포맷인데, 일반 PC에서 쓰기엔 사소한 문제가 있다.
오류를 방지하기 위해 내부적으로 계속 시간 코드를 삽입해놓았는데, 문제는 파일을 잘라붙여 편집한 경우, 이 시간 코드가 따라다닐 때가 있다는 것이다.
예컨데, 30분짜리 .ts 동영상이 있는데, 여기서 10분~20분의 10분 분량을 잘라낸다고 하자.

가운데 빨간색 영역을 잘라내면…


그러면, 새롭게 생성되는 .ts 파일은 20분짜리가 된다. (30-10 = 20)
그런데, 막상 새롭게 생성되는 .ts 파일을 재생해보면 20분짜리가 아니라 30분짜리라는 정보가 나온다.
그리고, 재생을 해보면 10분~20분이라는 영역은 없다고 나온다.

이런 식으로 나온다. 편집 도구 좀 잘 만드시지… 쩝


이런 문제(?)를 간편하게 해결하기 위해 .ts 파일을 재압축 없이 .mkv 파일로 변환해주는 툴을 하나 만들었다.
(이하 본 프로그램은 TS2MKV로 부름)

이 프로그램의 기능은 별 거 없다. mplayer를 이용해서 ts의 비디오/오디오 raw data를 추출하고 다시 합치는 것.
그리고, 이 과정에서 A/V 딜레이 정보를 확인해서 맞춰주는 것 뿐이다.

예컨데, 아래와 같이, 56분 29초짜리라고 정보가 표시되는 .ts 파일을…


TS2MKV로 변환하면 아래와 같이 16분 1초라고 정상적으로 표시된다.
또한, 아직까지는 .ts 재생필터에 비해 .mkv의 재생필터가 더 빠릿빠릿하게 동작한다는 장점도 얻을 수 있다.


이 프로그램은 아래 링크에서 다운받을 수 있다.



덧1. 이 프로그램을 만들면서 이해가 되지 않는 상황을 하나 발견했다.
H.264의 raw 데이터에 mkv/mp4 컨테이너를 씌우면 2배 느리게 재생되는 현상이 종종 발생했다.
즉, 30fps라면 강제로 60fps로 지정해야 정상재생이 되는 것이다.

그런 현상이 발생하지 않을 때도 있어 정확한 원인은 찾지 못했고, FPS에 관련된 옵션을 추가했다.
 - FPS 2배로 늘이기: FPS를 2배로 늘임. 즉, 2배 빠르게 재생됨.
 - FPS를 지능적으로 보정: 생성된 mkv 파일과 별도로 프레임 수를 강제로 조정한 mkv 파일을 하나 더 생성함.

참고로, 일부 문제 있는 파일을 DGAVCIndex로 읽어보면 정상적인 정보가 나온다. 어쩔…

덧2. 이 프로그램은 카라 팬인 친구가 카라 동영상 관련 부탁을 해서 만든 것이다.
그래서, 이 프로그램의 아이콘은 카라다. ㅋㅋㅋ

덧3. 친구야! 술 사라! 공짜가 젤 비싸다. ㅋㅋㅋ


2010년 12월 2일 목요일

멀고도 험했던 코딩용 글꼴 선택기

개발용 글꼴을 선택하는 것은 결코 쉽지가 않다.

일단, 고정폭 글꼴 중에 선택하는 것이 기본이다.
물론, 숫자 0과 알파벳 O가 쉽게 구분되는가, 대문자 I, 소문자 l, 숫자 1이 쉽게 구분되는가 등도 중요 기준이다.
게다가, 한글이 제대로 표시되는가도 확인해야 되고, 한자, 유럽어 등의 표시도 확인해야 된다.

보통 잘 만들어진 개발용 글꼴에 한글 글꼴을 fontlink 기능을 이용해서 연결하는 것이 일반적이었다.
하지만, 이 바닥에 혜성처럼 등장한 강자가 바로 (내가 굉장히 싫어하는) 네이버나눔고딕코딩이었다.

나눔고딕코딩을 사용하면 일단 fontlink라는 기능에 대해 몰라도 되는 것 같다. 얼마나 대단한가?

하지만, 나눔고딕코딩은 결코 만능이 아니다.
단지, 한글과 영문자가 나름 예쁘게 만들어져 있을 뿐 한자나 유럽어 등이 하나도 들어있지 않은 것이다.
그래놓고선, 개발용 글꼴이라니… 역시 네이버라는 생각이 들었다.

각설하고, 내가 좌충우돌 끝에 선택한 글꼴은 Consolas + 굴림체 + MS UI Gothic + PMingLiU + SimSun.

깔끔하고 예쁜 글꼴 + 한자/유럽어 칼지원


글꼴 자체가 예쁜 것은 물론이고, 필요로 하는 글꼴들이 모두 윈도우 XP에 내장되어 있다는 것이 가장 큰 장점이었다.
유일하게 추가로 설치해야 되는 Consolas는 MS 홈페이지에서 다운받을 수 있다.


차선책으로 선택한 것은 나눔고딕코딩 + Vera Sans Mono + PMingLiU + SimSun + 굴림체 + MS UI Gothic.


나눔고딕코딩에서 지원하지 못하는 예쁜 유럽언어를 Vera Sans Mono를 통해 표시하는 것이다.
나눔고딕코딩과 Vera Sans Mono를 다운받아야 하는 번거로움이 있고, 글꼴의 크기를 키우면 크기가 미묘하게 맞지 않는 단점은 있지만, 전체적으론 준수해보인다.


덧. 위에서 띄운 창을 나눔고딕코딩으로 설정하면 아래와 같다.
     이게 뭐니, 이게? 응?

유럽어를 다 날려버리는 무식한 글꼴. 결국 아스키+한글 뿐.


2010년 11월 20일 토요일

유니코드를 처리하지 못하는 Subtitle Workshop

by (Sleepy) | 2008/12/08 19:15

자막 작업의 왕, Subtitle Workshop. from IT, PC/• Software 2008/12/08 19:15. 오늘 소개할 Subtitle Workshop은 감히 자막 편집기의 최고봉이라 말할 수 있을 것 같다. 자막 제작, 수정, 변환, 오류정정에 이르기까지 자막에 관련된 모든 작업을 지원 ...


Subtitle Workshop은 자막을 제작하고, 다른 포맷으로 변환하는 데 있어 굉장한 능력을 보여준다.
아마도 자막 관련 통합 툴 중에서 이만한 완성도를 보이는 프로그램은 없었던 것 같다.

하지만, 유니코드를 전혀 처리하지 못하는 치명적인 단점이 있다.


1. 유니코드(BOM) SAMI 미지원

이 점에 대해 우선 분명히 짚어야 할 점은 유니코드 SAMI라는 포맷 자체가 비정상적인 포맷이라는 것이다.

SAMI Structure and Declaration

Because SAMI is based closely on HTML, it has a similar (but not identical) component construct that makes it an easy format to learn and edit. All SAMI documents must begin with a <SAMI> tag and end with a </SAMI> tag.


<Understanding SAMI 1.0에서 발췌>


MS에서 작성한 Understanding SAMI 1.0에서 분명히 얘기하듯이 SAMI라는 포맷은 HTML에 기반을 둔 포맷이다.

하지만, HTML과 달리 텍스트 인코딩을 지정하는 기능이 없다.
SAMI의 한글 코드는 묵시적으로 확장 완성형 한글을 사용하고 있어 유니코드의 글자를 모두 표시할 수 없다.

우리나라의 사용자들은 이런 상황을 극복(?)하기 위해 그냥 SAMI 파일을 통째로 유니코드로 저장하기로 했다.
또한, 플레이어를 만드는 개발자들도 이에 동조해서 유니코드 포맷의 SAMI를 처리하고 있다.

그런데, SAMI의 기본을 생각해보면 SAMI에서 유니코드를 사용하려면 부호 있는 UTF-8을 사용했어야 했다.
하지만, 이젠 늦었다. 그냥 이렇게 갈 수 밖에 없다.

각설하고, 이 말도 안 되는 유니코드 SAMI는 Subtitle Workshop에서 읽을 수 없다.
그리고, 워낙에 말도 안 되는 포맷이라 앞으로도 Subtitle Workshop에서는 지원하지 않을 것이다.


2. SRT/UTF-8 미지원

이건 유니코드 SAMI와 완전히 다른 얘기다.

SRT 파일은 ANSI 포맷 외에, UTF-8 포맷으로도 저장할 수 있다.
특히, 아이폰 내장 비디오 플레이어는 UTF-8 포맷의 SRT 파일만 재생이 가능하다.

아이폰 재생 화면: 자막은 [아바타] 감성 버전. 영상은 전혀 무관한 [딱따구리]


즉, SRT는 SAMI와 다르게, 기본적으로 유니코드를 지원한다.

하지만, Subtitle Workshop 이 SRT/UTF-8를 전혀 읽거나 쓰지 못한다.


3. 글자 속성의 파괴

이 부분은 정말 이해가 되지 않는 부분이다.

SAMI는 (다른 자막 포맷과 마찬가지로) 다양한 글자 속성을 지원한다.
<b>, <i>, <font>, <u> 등등...
지원되는 속성와 형식은 SRT도 유사하다.

그런데, Subtitle Workshop는 어떤 글자 속성이든지 <i>로 대체해버린다.
난 한동안 SRT는 <i>밖에 지원하지 않는다고 생각할 정도였다.


결론적으로, Subtitle Workshop은 유니코드 SAMI를 SRT/UTF-8로 제대로 변환하지도 못하고, 글자 속성도 파괴한다.
자막 작업의 왕이라고 부르기엔 너무 커다란 헛점이 있는 것이다.

VCi를 만들면서 자막 변환 부분을 직접 제작한 이유가 바로 이런 점 때문이었다.
VCi는 유니코드 포맷 자막을 직접 읽어 완전하게 UTF-8 유니코드로 변환한다.

동영상 변환시 싱크 문제가 생기는 이유들

by BLUEnLIVE | 2010/11/18 07:46

아이폰 동영상 한방에 변환하는 툴: 완벽 버전 by BLUEnLIVE | 2010/11/15 01:21 아이폰(아이패드, 아이팟 터치)의 단점 중 하나는 동영상을...


1. 기본형: delay

비단 VCi(아이폰용 동영상 변환기) 뿐만 아니라, 동영상 변환 프로그램을 사용해보면 싱크 오류는 빈번하게 발생한다.

그런데, 대부분의 경우 동영상 변환시 싱크 오류가 발생하면 의외로 간단하게 해결할 수가 있다.
A/V muxing 도구들은 대부분 오디오 딜레이를 지정하는 기능이 있고, 딜레이 값만 적절히 지정하면 되는 것이다.

싱크 오류 발생시 원본의 딜레이를 가장 쉽게 확인할 수 있는 도구는 MediaInfo다.

MediaInfo에서 지원되는 동영상 포맷은 거의 무한하다. 단, SKM 같은 미친 포맷은 제외


이렇게 확인된 딜레이 값을 지정하기만 하면 된다.



2. 확장형: MP4Box/.mp4 문제

하지만, MP4Box로 .mp4를 만들 때는 얘기가 다르다.
일부 동영상을 완벽히 저장해주지 못하는 치명적인 문제가 있다.
(이것이 .mp4 포맷의 한계인지, MP4Box의 한계인지는 모르겠음)

아래 그림을 보자.


그림 1과 같은 원본 비디오를 MP4Box를 이용해서 mp4로 변환하면 맨 앞의 v0 영역을 날려버리는 경우가 있다.
그러면 신경써서 delay를 넣어주더라도 싱크는 깨지는 것이다.

이러한 경우는 원본 비디오와 생성된 비디오의 길이를 비교해서 이 값을 딜레이에 반영하는 것으로 해결할 수 있다.



3. 극악의 확장형: 원본 비디오가 잘린 경우

사실, 2번의 경우 자체는 거의 완벽한 해결책이 있다.
바로, 원본 비디오와 생성된 비디오의 길이 차이를 비교하는 것이다.

그런데, 여러가지 이유로 파일이 잘린 경우(700MB 중 300MB만 전송된 경우) 원본 비디오의 길이를 알 수 없다.
이 경우는 여러가지 꼼수를 사용해서 원본 비디오의 정보를 추정해야 한다.

하지만, 한 방에 해결되는 완벽한 솔루션존재하지 않는다.

게다가, 진짜 문제는 2번인지 3번인지 (또는 1번인지)를 판단하는 것 자체가 쉽지 않다는 것이다.

VCi는 나름 여러가지 방법으로 싱크를 조절하고 있다.
최대한 싱크를 맞추기 위해 노력하고 있지만, 그래도 완벽한 솔루션은 없다.

그렇기 때문에 [VCi]와 별도로 싱크를 보정한 파일인 [VCi-DC]를 생성하는 것이다.

2010년 10월 25일 월요일

오디오/비디오 싱크를 위한 리샘플 헬퍼

수작업으로 비디오를 편집하다보면 오디오의 길이를 조절해야 되는 경우를 만날 때가 있다.
예를 들면 이런 경우...

캠코더로 아이들의 동영상을 찍었는데, 여기에 비틀즈의 <Yesterday>를 입히고 싶다.
그런데, 아이들이 등장하는 장면에서 첫 소절이 시작되게 하고, 사라지는 장면에서 마지막 소절이 시작되게 하고 싶다.
그런데, 노래는 약 2분, 동영상은 1분 50초...

영상에 오디오를 맞추는 것이 일반적이지는 않지만, 이런 경우에는 오디오의 길이를 약간 줄여서 영상에 맞춰야 된다.

그리고, 오디오의 길이를 조절하는 방법은 많다.
굳이 유료 프로그램을 어둠의 경로로 받지 않아도 무료 프로그램도 있고, 간단하게 프로그램을 하나 만들어도 된다.

문제는... 오디오를 얼마만큼 늘이거나 줄여야 되는지를 알기 힘들다는 것이다.

이런 경우가 종종 있어 일일이 손으로 계산하다가, 마침내 귀차니즘을 이기지 못해서 하나 만들었다.

길이를 조절한 뒤에 앞에 73.685초의 오디오 추가


이 프로그램의 기능은 무척 단순하다.
위에서 보는 바와 같이 두 지점의 시간과 그 두 지점이 이동할 시간을 입력하면 적절한 식을 만들어준다.
또한, 원본 오디오의 시간을 입력하면 오디오의 길이를 얼마로 바꿔야 하는지도 함께 알려준다.

남은 일은 적절한 프로그램으로 오디오를 읽은 뒤 길이를 조절하고서 앞을 잘라주거나 무음을 추가하는 것 뿐이다.

이 프로그램은 아래 링크에서 다운받을 수 있다.


2010년 9월 26일 일요일

한글 풀어쓰기→모아쓰기를 통해 얻은 지식들

by BLUEnLIVE | 2010/09/24 09:30

아이폰은 단점이 없는 완벽하고도 지고지순한 폰이 아니다. 철학이 뚜렷하고, 이에 따른 장단점이 명확히 나뉘는 폰이다. 그런데, 그런 원인...



1. 유니코드의 한글자소는 초중종성 정보가 있는 것과 없는 것이 별도로 존재

유니코드에는 (당연히) 음절 단위의 글자 외에도 한글자소가 별도로 존재한다.
그런데, 이 자소에는 초중종성의 정보가 있는 자소와 없는 자소가 별도로 존재한다.

일단, 정보가 있는 자소U+1100 부터 시작된다.
현대어→고어 순으로 할당되어 있으며, 현대어에는 ㅄ같은 문자가 초성에 할당되어있지 않았다.

초중종성 정보가 포함된 한글자소: U+1100부터 시작됨. 아래 표시된 부분은 종성 'ㄱ'


초중종성 정보가 없는 자소U+3131 부터 시작된다.
여기는 자음에 초성과 종성이 혼합되어 있으며, 이 순서는 완성형 한글의 순서와 정확히 일치한다.



2. 두 종류의 자소(U+1100, U+3131)를 완성형으로 변환시 문제 발생 가능

VC6은 기본적으로 유니코드가 아닌 완성형 코드를 지원한다.
그리고, 시스템에서 이를 유니코드로 변환할 때는 U+3131로 변환한다.
즉, 아래와 같은 코드를 실행시켰을 때...

CFileFind finder;
BOOL bOk = finder.FindFile(_T("c:\\*.txt"));
while (bOk)
{
  bOk = finder.FindNextFile();
  if (finder.IsDots() || finder.IsDirectory()) continue;
  _trename(finder.GetFilePath(), _T("c:\\말도안돼.txt"));
}

c:\ㅁㅏㄹㄷㅗㅇㅏㄴㄷㅙ.txt가 존재하더라도 한글자소가 U+1100으로 저장되어 있다면 rename되지 않는다.
왜냐하면 _trename()의 내부에서는 이런 순서로 진행되기 때문이다.

API: U+1100 → finder.GetFilePath(): 완성형 변환_trename(API): U+3131 변환

따라서, 멀쩡히 있는 파일명을 API를 통해 읽은 뒤 다른 이름으로 바꾸는 작업이 불가능할 때도 있는 것이다.
물론, 해결책은 있고, 간단하다. 컴파일러를 유니코드 모드로 설정하면 된다.


3. 유니코드 한글은 조합형으로 구성되었음

한글을 조합하는 방법에 대해 이리저리 고민했는데, 알고보니 유니코드의 한글 코드는 조합형으로 구성되어있다.
보통 조합형 한글이라는 개념은 2바이트(16비트)를 1비트(무조건 1)+5비트(초성)+5비트(중성)+5비트(종성)으로 구성하는 방식을 의미한다.


하지만, 유니코드의 한글은 이런 비트를 조합하는 방식은 아니다. (그런 테이블을 할당받을 수도 없음)
U+AC00부터 초성 19자, 중성 21자, 종성 28자를 순서대로 배치하는 것이다.


예컨데, 이라는 글자의 코드는 U+AC00 + 7(ㅂ)*21*28 + 15(ㅞ)*28 + 9(ㄺ) = U+DBC1이다.
이러한 원리를 이용해서 간단한 식만으로 한글을 조합하거나, 자소를 분리해낼 수 있는 것이다.

유니코드의 각국어 할당표를 만드는 과정에서 여러나라들의 수많은 알력이 있었다.
유니코드의 한글구성표를 만드는데 노력하시고, 박터지게 싸우신 모든 분들께 감사드린다.


덧. VC++ 6.0도 유니코드를 기본으로 사용할 수 있음

메뉴에서 Project → Settings... 선택한 뒤, C/C++ 탭에서 Preprocessor definitions:를 찾는다.
여기서 _MBCS를 찾아 _UNICODE로 바꾼다.


다음으로, Link 탭에서 Output을 선택한 뒤 Entry-point symbol:wWinMainCRTStartup으로 지정한다.


2010년 8월 25일 수요일

캡쳐한 거대 위성사진 쉽게 자르기 (goohwan님 전용) 업데이트(100825)

QAOS에 다음 위성사진 스크린샷 하는 방법좀 알려주세요라는 goohwan 님의 절규어린 질문이 올라왔다.
해결책은 의외로 간단했다.

Firefox + Screengrab! 조합이었다.

그런데, 문제는 이렇게 캡쳐된 이미지의 크기가 무려 6152x6229인데, 주변 여백을 일일이 잘라내야 된다는 거...
작은 이미지라도 잘라내는 것이 번거로운데, 이렇게나 커다란 놈을 잘라내는 건 귀차니즘의 극치다...

이 놈의 원본이 무려 6152x6229, 잘라내고 남은 건 6144x6144다


그래서 프로그램을 하나 간단하게 만들었다. 잘라내야 하는 여백의 크기가 일정해서 만드는 것 자체는 큰 문제가 없었다.
문제가 있었다면... 주말에 회사에 일 하러 들어와서 만들었다는 거...
(혹시 오해는 마시길... 할 일 다 한 다음에 집에 안 가고 만든 거임)

기본값은 물론 다음 Open API + Screengrab!에 맞춰져있음


아래 파일을 다운받은 뒤에 압축을 풀면 된다.
실행파일 하나만 달랑 들었고, 설정 저장 같은 기능은 없다. 1시간 동안 후다닥 만든 프로그램이라... ㅎㅎ


덧2. 1.2로 업데이트됨. 잘라내기 범위를 버튼으로 늘이거나 줄일 수 있으며, jpeg 라이브러리를 빠른 놈으로 교체함.
(2010. 8. 25)

2010년 7월 11일 일요일

Notepad2 4.1.24 정식버전 관련 패치 모음

by BLUEnLIVE | 2010/06/26 16:13

notepad2 4.1.24 패치버전 공개 (한글판) by BLUEnLIVE | 2010/03/08 21:10 notepad2 4.1.24 패치버전 공개 (영문판) by BLUEnLIVE | 2010/0...



notepad2 4.1.24 한글 패치에 적용한 패치들을 정리했다.


0. 들어가기 전에

다음과 같은 내용은 모두 준비가 되어있다고 가정하고 설명함.

a. Visual C++ v6.0 및 Service Pack 6 설치
b. Platform SDK 설치 (Windows® Server 2003 R2 Platform SDK ISO Download)
c. Scintilla 2.03 다운로드. 현재 Scintilla는 2.12까지 공개되었으나, 2.03을 사용해야 함


1. Kai Liu님의 패치 적용

np2-4.0.22-allpatches.7z 패치를 적용한다.
이 패치는 총 13개의 패치로 이루어져있으며, 코드 폴딩이나 각종 문법을 포함한 다양한 패치로 구성되어있다.

문제는 이 패치는 Notepad2 4.0.22, 그것도 beta5에 적용 가능한 패치라는 거.
대부분은 그대로 적용가능하고, 일부는 눈치껏(?) 적용할 수 있으나, 2개는 완전히 바뀐 구조에 따라 일일이 적용해야 한다.
특히 문법 패치의 경우, 바뀐 포맷을 유심히 본 후, 문자열 중에 기존 문법에 있는 부분은 그대로 사용해야 되고, 없는 부분은 일일이 notepad2.rc에 문자열을 추가해야 한다.

사용자 삽입 이미지

더 이상의 자세한 설명은 생략한다.


패치 대상 세부 내역 보기..



2. 한글 IME 패치

간단하게 단 하나로 끝난다. (아싸!)

a. notepad2 컴파일 삽질기 8 : 한글 IME 패치


3. 기능 추가

okto 님께서 필요하다고 닥달한 기능들이며, 귀차니 님이 큰 축을 담당하시고, 나도 중간중간 밥숟갈을 들었음.

a. notepad2 컴파일 삽질기 부록#5.2 : context-menu 기능 수정

b. notepad2 컴파일 삽질기 부록#6.1 : 메모장 대신 사용 (수정)

c. notepad2 컴파일 삽질기 부록#4 : AutoHotkey 스킴 추가
   이것 역시 앞의 1번에 따라 열심히 패치한다. 역시 설명은 생략한다. ㅋㅋ

d. notepad2 컴파일 삽질기 부록#11 : 포트란 스킴 추가

e. notepad2 컴파일 삽질기 부록#7 : Shift+마우스 휠에 Page Up/Down 기능 부여

f. notepad2 컴파일 삽질기 부록#9 : 다음 바꾸기(Replace Next) 메뉴에 추가

g. notepad2 컴파일 삽질기 부록#10 : 시간/날짜 삽입 순서 변경

h. notepad2 컴파일 삽질기 부록#12 : NBSP 관련 수정  (2010. 7. 11 추가)

그런데... 패치를 정리한 것 맞나? 뭔가 좀...


notepad2 4.1.24 패치버전 재공개 (한글판)

by BLUEnLIVE | 2010/03/08 21:10

notepad2 4.1.24 패치버전 공개 (영문판) by BLUEnLIVE | 2010/03/07 10:48 지난 3월 2일 드디어 notepad2가 4.1.24로 업데이트되었다....


notepad2 4.1.24의 한글 패치를 공개한지 넉달이 되어간다.
그동안 notepad2 자체는 패치되지 않았지만, Scintilla도 2.12로 업데이트 되고, 기타 수정사항도 있어 손을 좀 댔다.

그런데… Scintilla 2.12를 적용하고 나니 찾기 기능이 잘 동작하지 않았다.
급하게 다시 Scintilla 홈페이지를 뒤져봤더니…

DBCS 환경에서는 무조건 case-sensitive 검색만 지원


DBCS 환경에서는 case-sensitive 검색만 지원하도록 수정되었단다. 헐~

그래서 다시 2.03으로 회귀…
하지만, 문법 분석기(lexer)는 여러모로 업데이트 된 것이 있어 그건 2.12의 것을 사용하기로 했다.


1. Scintilla 2.12중 lexer 적용

얼마 전(정확히는 지난 6월 1일) Scintilla 라이브러리가 2.12로 업데이트 되었다.
속도가 일부 향상되고, 약간의 버그가 수정되었다.

하지만, 전술했듯이 검색 기능이 정상적으로 동작하지 않아 다시 2.03으로 회귀했다.
하지만, lexer는 여러모로 업데이트 되었기 때문에 lexer(Lex*.cxx)들과 SciLexer.h만 2.12의 것을 사용했다.


2. 포트란 스킴 추가

notepad2 컴파일 삽질기 부록#11 : 포트란 스킴 추가에서 설명한 포트란 스킴을 추가했다.


3. 파일명이 notepad.exe인 경우 "메모장 대신 사용" 기능 미동작

(구운고구마 님께서 지적해주신 문제임)
동작 원리상, 파일명을 notepad.exe로 바꾼 경우 메모장 대신 사용을 적용할 수 없다.
파일명이 notepad.exe인 경우 메모장 대신 사용 기능을 선택하면 경고벨소리를 들려주도록 수정했다.
※ 관련 포스트: notepad2 컴파일 삽질기 부록#6.1 : 메모장 대신 사용(수정)


4. NBSP를 ASCII에서 붙이면 공백으로 자동 변환

NBSP(Non-break space)를 ASCII 모드에서 붙일 때 공백으로 변환하도로 수정했다.
※ 관련 포스트: notepad2 컴파일 삽질기 부록#12 : NBSP 관련 수정  (2010. 7. 11 추가)


이렇게 수정된 notepad2는 아래 링크에서 다운받을 수 있으며…


동작화면은 아래와 같다.


notepad2 컴파일 삽질기 부록#12 : NBSP 관련 수정

SMI 포맷의 자막을 만들거나, HTML을 직접 작성해본 경험이 있는 사람은 &nbsp;라는 표현을 자주 봤을 것이다.
그런데, NBSP가 뭔지 정확하게 아는 사람은 그리 많지 않은 것 같다.

NBSP는 Non-break Space의 약자로 공백의 일종이지만, 사실 일반적인 공백(0x20)과는 다르다.
줄바꿈을 하지 않는 공백을 의미하며, 유니코드에서는 0xa0이라는 값에 할당되어 있고, ASCII에는 이 개념이 없다.

유니코드를 ASCII 코드로 변환할 때 ASCII 코드에 없는 문자를 변환하는 방식은 정의되어있지 않다.
따라서 표준에 따르면 NBSP를 ASCII로 변환하면 공백으로 만들던, 물음표로 만들던 문제가 되지 않는다.

하지만, 실세계에서는 NBSP는 알아서 공백(0x20)으로 변환해주는 것이 예의다.

notepad2는 WideCharToMultiByte() 함수를 이용해서 유니코드를 ASCII 등으로 변환한다.
그런데, 이 함수에선 NBSP의 변환을 별도로 처리하지 않는다.

따라서, NBSP를 ASCII 모드에서 붙여넣으면 아래와 같은 결과를 보여준다.

구글 크롬에서 Syntax Highlighter 3.0으로 처리한 소스를 복사-붙이기 한 화면


웹 브라우저 중에서 IE나 FireFox에서 복사(copy)를 하면 NBSP를 공백으로 변환한 뒤 클립보드로 보내지만, 구글 크롬은 NBSP를 그대로 내보낸다.
Syntax Highlighter에서 코드를 복사한 뒤에 메모장2에 붙여넣으면 이 현상을 경험할 수 있다.

전술했듯이, 이 현상은 결코 버그가 아니며, 단지 현상일 뿐이다.
하지만, 사용자 입장에서 불편하긴 마찬가지다.

그래서 메모장2를 패치하기로 했다.


1. ScintillaWin.cxx 수정


이 현상을 수정하려면 일단 붙여넣기 부분을 수정해야 된다.
ScintillaWin.cxx 파일에서 void ScintillaWin::Paste() 함수를 찾은 뒤 아래 부분의 위치를 확인한다.

  UINT cpDest = CodePageFromCharSet(
      vs.styles[STYLE_DEFAULT].characterSet, pdoc->dbcsCodePage);

  len = ::WideCharToMultiByte(cpDest, 0, uptr, -1,
      NULL, 0, NULL, NULL) - 1; // subtract 0 terminator

이 부분을 아래와 같이 수정한다. 몇 줄의 코드를 삽입하는 것이며, 기존 코드의 수정은 없다.

  UINT cpDest = CodePageFromCharSet(
      vs.styles[STYLE_DEFAULT].characterSet, pdoc->dbcsCodePage);

  // 여기부터 삽입
  int wlen = wcslen(uptr);
  for (int i=0; i<wlen; i++)
  {
      if (uptr[i] == 0xa0) uptr[i] = 0x20;
  }
  // 여기까지 삽입

  len = ::WideCharToMultiByte(cpDest, 0, uptr, -1,
      NULL, 0, NULL, NULL) - 1; // subtract 0 terminator

이것으로 붙여넣기에서 문제는 해결되었다.


2. Edit.c

사실, 이 현상을 경험할 수 있는 것은 붙이기 명령 외에 하나가 더 있다.

유니코드에서 ASCII로 코드 페이지를 바꾸면 이 현상이 발생할 수 있다.
그래서, 코드 페이지를 바꿀 때도 이 문제가 발생하지 않도록 수정한다.

BOOL EditConvertText(...) 함수를 찾아 아래 내용의 위치를 확인한다.

  cbwText  = MultiByteToWideChar(cpSource,0,pchText,length,pwchText,length*3+2);
  cbText   = WideCharToMultiByte(cpDest,0,pwchText,cbwText,pchText,length*5+2,NULL,NULL);


이 내용을 아래처럼 수정한다. 역시 몇 줄의 코드를 삽입하는 것이며, 기존 코드의 수정은 없다.

  cbwText  = MultiByteToWideChar(cpSource,0,pchText,length,pwchText,length*3+2);
  // 여기부터 삽입
  {
    int i;
    for (i=0; i<length; i++)
        if (pwchText[i] == 0xa0) pwchText[i] = 0x20;
  }
  // 여기까지 삽입
  cbText   = WideCharToMultiByte(cpDest,0,pwchText,cbwText,pchText,length*5+2,NULL,NULL);

이렇게 수정한 뒤 구글 크롬 메모장2로 복사-붙이기를 실행하면 아래와 같이 동작하는 모습을 볼 수 있다.

구글 크롬 따위는 겁나지 않는다능!


2010년 6월 26일 토요일

notepad2 컴파일 삽질기 부록#6.1 : 메모장 대신 사용(수정)



okto님 배포본 notepad2는 설치 및 윈도우 메모장 대체를 위해 notepad.inf 파일을 이용했다.
이 방식은 설치가 쉽다는 장점은 있지만, 설치 제거에 손이 많이 간다는 단점이 있다.

장점을 살리고 단점을 제거하기 위해 구차니님께서 메모장을 대체하는 기능을 구현해주셨다.

그런데, 이를 위해 제작/공개한 notepad2 컴파일 삽질기 부록#6 : 메모장 대신 사용에는 사소한 버그가 하나 있었다.
바로 파일명을 notepad.exe로 바꾼 상태에선 동작하지 않는 다는 것이다.

이 부분은 사실 당연한 것이다.
이 기능은 레지스트리의 Image File Execution Options 키를 이용해서 notepad.exe라는 프로그램을 실행하면 다른 프로그램(notepad2.exe)을 실행하도록 지정하는 것인데, 그 프로그램이 또 notepad.exe라서 발생하는 문제이다.

이 기능을 사용하려면 소스 코드를 아래와 같이 수정하면 된다.

반드시 삽질기 부록#5.1 : context-menu 통합 및 수정을 적용한 후에 이 수정을 해야 함

수정 대상 파일은 역시 resource.h, Notepad2.rc, Notepad2.c 세 개이다.


1. resource.h

다음 줄을 추가한다.
#define IDM_REPLACE_NOTEPAD             40692



2. Notepad2.rc

아래 내용을 찾는다.
MENUITEM "Add context-menu to all file type", IDM_REGISTRY_ALL
, CHECKED
바로 앞에 다음 줄을 추가한다.
MENUITEM "Replace Notepad",             IDM_REPLACE_NOTEPAD



3. Notepad2.c

앞부분에 다음 변수를 하나 선언한다.
BOOL      bReplaceNotepad;


다음, void MsgInitMenu(HWND hwnd,WPARAM wParam,LPARAM lParam)에서 아래 내용을 찾는다.
CheckCmd(hmenu,IDM_REGISTRY_UNKNOWN,bRegistryUnknownType);
또는
CheckCmd(hmenu,IDM_REGISTRY_ALL,bRegistryAllType || bRegistryUnknownType);
바로 앞에 다음 줄을 추가한다.
CheckCmd(hmenu,IDM_REPLACE_NOTEPAD,bReplaceNotepad);


다음, LRESULT MsgCommand(HWND hwnd,WPARAM wParam,LPARAM lParam) 함수에 다음 case 문을 추가한다.
이 함수 전체가 하나의 switch-case 문으로 되어있는데, case를 하나 추가하는 것이다.
case IDM_REPLACE_NOTEPAD:
if (bReplaceNotepad)
{
//WinNT requires the key to have no subkeys
RegDeleteKey(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\notepad.exe");
bReplaceNotepad = FALSE;
}
else
{
WCHAR path[MAX_PATH], *wTemp;
GetModuleFileName(GetModuleHandle(NULL), path, MAX_PATH);
bReplaceNotepad = TRUE;

wTemp = path;
while (*wTemp) wTemp++;
if (_wcsicmp(wTemp-12, L"\\notepad.exe"))
{
HKEY key1;
LONG res = RegCreateKey(HKEY_LOCAL_MACHINE,L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\notepad.exe",&key1);
if (res == ERROR_SUCCESS)
{
WCHAR cmd[MAX_PATH + 4];
int len = wsprintf(cmd, L"\"%s\" /z", path);
RegSetValueExW(key1, L"Debugger", 0, REG_SZ, (const unsigned char*)cmd, len * 2);
}
else
{
bReplaceNotepad = FALSE;
MessageBeep(MB_ICONEXCLAMATION);
}
RegCloseKey(key1);
}
else
{
bReplaceNotepad = FALSE;
MessageBeep(MB_ICONEXCLAMATION);
}
}
break;


마지막으로 void CheckRegistry() 함수의 마지막 부분에 다음 내용을 추가하면 된다.
res = RegOpenKey(HKEY_LOCAL_MACHINE,L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\notepad.exe",&key);
if(res == ERROR_SUCCESS)
        bReplaceNotepad = TRUE;
else    bReplaceNotepad = FALSE;
RegCloseKey(key);


수정된 결과는 아래와 같다. 짜잔~

사용자 삽입 이미지

수정에 수고해주신 구차니님께 다시 한 번 감사드립니다.

notepad2 컴파일 삽질기 부록#5.2 : context-menu 기능 수정

삽질기 부록#5.1 : context-menu 통합 및 수정을 수정한 포스팅. 부록 #5/#5.1과 본 포스트는 동시 적용불가.

구차니님께서 수고해주신 덕분에 context 메뉴(오른쪽 버튼 클릭 메뉴)를 통해 Notepad2를 사용할 수 있었다.
(삽질기 부록#1 : context-menu 추가삽질기 부록#2 : context-menu 추가 업그레이드 참조)

그런데, Notepad2 3.1.21 정식버전에서는 이 수정을 미묘하게 바꿔야 했으며, 이후 okto님구차니님과 context 메뉴에 대한 수정을 논의한 결과 메뉴를 조금 간결하게 변형하기로 했다.
(File 메뉴에 있는 context-menu 관련 메뉴를 하나만 남기도록 수정)

그리고, 레지스트리에 등록이 실패했을 때의 코드가 빠져있었는데, 실패했을 경우 간단한 경보음을 들려주도록 했다.

삽질기 1~3을 모두 적용했다고 가정하고 시작한다.
그리고, notepad2 컴파일 삽질기 부록#5.1 : context-menu 통합 및 수정은 적용하지 않은 상태에서 시작한다.


수정 대상파일은 resource.h, Notepad2.rc, Notepad2.c의 3개이며, 모두 src 폴더에 저장되어 있다.


1. resource.h

다음 한 줄을 추가.
#define IDM_REGISTRY_ALL            40690


2. Notepad2.rc

IDR_MAINWND MENU DISCARDABLE에서 아래 내용을 찾는다.
MENUITEM "Propert&ies...",              IDM_FILE_PROPERTIES
MENUITEM "Create &Desktop Link",        IDM_FILE_CREATELINK
MENUITEM SEPARATOR
POPUP "&Favorites"
BEGIN
    MENUITEM "&Open Favorites...\tAlt+I",   IDM_FILE_OPENFAV
    MENUITEM "&Add Current File...\tAlt+K", IDM_FILE_ADDTOFAV
    MENUITEM "&Manage...\tF9",              IDM_FILE_MANAGEFAV
END
이 부분을 아래와 같이 수정한다.
MENUITEM "Propert&ies...",              IDM_FILE_PROPERTIES
MENUITEM SEPARATOR
MENUITEM "Create &Desktop Link",        IDM_FILE_CREATELINK
MENUITEM "Add context-menu to all file type", IDM_REGISTRY_ALL, CHECKED
MENUITEM SEPARATOR
POPUP "&Favorites"
BEGIN
    MENUITEM "&Open Favorites...\tAlt+I",   IDM_FILE_OPENFAV
    MENUITEM "&Add Current File...\tAlt+K", IDM_FILE_ADDTOFAV
    MENUITEM "&Manage...\tF9",              IDM_FILE_MANAGEFAV
END

구분자 하나와 context-menu를 추가하는 내용임.


3. Notepad2.h

새로운 함수 하나를 추가.
void CheckRegistry();


4. Notepad2.c

파일 앞부분의 변수 선언부에 다음 내용을 추가.
BOOL      bRegistryUnknownType;
BOOL      bRegistryAllType;


다음, LRESULT CALLBACK MainWndProc(HWND hwnd,UINT umsg,WPARAM wParam,LPARAM lParam)에서 아래 내용을 찾는다.
case WM_COMMAND:
  return MsgCommand(hwnd,wParam,lParam);
여기에 아래와 같이 한 줄을 추가한다.
case WM_COMMAND:
  CheckRegistry();
  return MsgCommand(hwnd,wParam,lParam);


다음, void MsgInitMenu(HWND hwnd,WPARAM wParam,LPARAM lParam)에서 아래 내용을 찾는다.
EnableCmd(hmenu,IDM_EDIT_SELTONEXT,i);
EnableCmd(hmenu,IDM_EDIT_SELTOPREV,i && lstrlenA(efrData.szFind));
EnableCmd(hmenu,IDM_EDIT_REPLACE,i /*&& !bReadOnly*/);

CheckCmd(hmenu,IDM_VIEW_USE2NDDEFAULT,Style_GetUse2ndDefault(hwndEdit));
이 부분을 아래와 같이 수정한다. 1줄을 추가하는 것임.
EnableCmd(hmenu,IDM_EDIT_SELTONEXT,i);
EnableCmd(hmenu,IDM_EDIT_SELTOPREV,i && lstrlenA(efrData.szFind));
EnableCmd(hmenu,IDM_EDIT_REPLACE,i /*&& !bReadOnly*/);

CheckCmd(hmenu,IDM_REGISTRY_ALL,bRegistryAllType || bRegistryUnknownType);

CheckCmd(hmenu,IDM_VIEW_USE2NDDEFAULT,Style_GetUse2ndDefault(hwndEdit));


다음은 LRESULT MsgCommand(HWND hwnd,WPARAM wParam,LPARAM lParam)의 수정임.
이 함수는 전체가 하나의 switch-case 문으로 되어있는데, 이 case 중에 IDM_REGISTRY_ALL을 추가함.
case IDM_REGISTRY_ALL:
// Register registry - type all
if (bRegistryAllType || bRegistryUnknownType)
{
if (bRegistryAllType)
{
  //WinNT requires the key to have no subkeys
RegDeleteKey(HKEY_CLASSES_ROOT, L"*\\shell\\Open with notepad2\\command");
RegDeleteKey(HKEY_CLASSES_ROOT, L"*\\shell\\Open with notepad2");
bRegistryAllType = FALSE;
}
if (bRegistryUnknownType)
 {
//WinNT requires the key to have no subkeys
RegDeleteKey(HKEY_CLASSES_ROOT, L"Unknown\\shell\\Open with notepad2\\command");
RegDeleteKey(HKEY_CLASSES_ROOT, L"Unknown\\shell\\Open with notepad2");
bRegistryUnknownType = FALSE;
}
}
else
{
HKEY key1;
LONG res = RegCreateKey(HKEY_CLASSES_ROOT,L"*\\shell\\Open with notepad2",&key1);
bRegistryAllType = TRUE;
if (res == ERROR_SUCCESS)
{
WCHAR cmd[] = L"Open with &notepad2";
RegSetValue(key1, NULL, REG_SZ, cmd, wcslen(cmd));
}
else bRegistryAllType = FALSE;
RegCloseKey(key1);

  res = RegCreateKey(HKEY_CLASSES_ROOT,L"*\\shell\\Open with notepad2\\command",&key1);
if (res == ERROR_SUCCESS)
{
WCHAR cmd[MAX_PATH + 4];
WCHAR path[MAX_PATH];
int len;
GetModuleFileName(GetModuleHandle(NULL), path, MAX_PATH);
len = wsprintf(cmd, L"%s %%1", path);
RegSetValue(key1, NULL, REG_SZ, cmd, len);
}
else bRegistryAllType = FALSE;
RegCloseKey(key1);

if (!bRegistryAllType)
{
MessageBeep(MB_ICONEXCLAMATION);
}
}
break;


마지막으로 함수를 하나 추가한다.
void CheckRegistry()
{
    HKEY key;
    LONG res;

    res = RegOpenKey(HKEY_CLASSES_ROOT,L"*\\shell\\Open with notepad2",&key);
    if(res == ERROR_SUCCESS)
            bRegistryAllType = TRUE;
    else    bRegistryAllType = FALSE;
    RegCloseKey(key);

    res = RegOpenKey(HKEY_CLASSES_ROOT,L"unknown\\shell\\Open with notepad2",&key);
    if(res == ERROR_SUCCESS)
            bRegistryUnknownType = TRUE;
    else    bRegistryUnknownType = FALSE;
    RegCloseKey(key);
}

이렇게 수정하면 아래처럼 File 메뉴에서 context-menu 메뉴 하나가 표시된다.

사용자 삽입 이미지

context-menu가 하나만 나오도록 수정. ^^;


디카 메모리가 부족하다고 풍경을 마음에 담을 순 없다! (캐궁극 버전)

by BLUEnLIVE | 2009/10/14 04:17

이번에도 okto님의 꼬득임에 문득 넘어가버려 기능 추가. OTL (이번에도 너는 이미 뜯어고치고 있다!) 내가 킹크랩을 반드시 뺏어먹고야 말...


디카 사진의 크기를 줄여주는 프로그램에 jpeg SIMD extension 라이브러리를 적용했다.
덕분에 기존 버전에 비해 속도가 엄청나게 빨라졌음은 물론, 파일의 크기가 훨씬 작아졌다.

기존 버전도 120,832바이트라는 엄청나게 작은 크기를 보여줬지만, 이 버전은 단  98,304바이트밖에 되지 않는다!

이 프로그램은 정말 작기 때문에 여행을 갈 때 디카 메모리 한 구석에 넣어두고 쓰면 유사시에 큰 도움이 될 것이다!


물론, 실행 화면은 아래처럼 거의 바뀐 게 없다. ㅎㅎ


notepad2 컴파일 삽질기 부록#11 : 포트란 스킴 추가

회사에서 일을 하면서 포트란 소스를 C++로 변환할 일이 생겼다.
notepad2에서 포트란 스킴을 지원하는 줄 알았는데, 지금 보니 지원하지 않았다. 헐~

그래서 포트란 스킴을 추가하기로 했다.
notepad2에서 사용하는 Scintilla에서는 포트란 스킴은 지원하지만, 이를 notepad2에서 사용하지 않는 것이다.
따라서, 이를 사용하기 위해서는 조금만 수정하면 된다.

포트란 스킴을 사용하려면 아래와 같이 수정하면 된다.


1. Notepad2.rc 수정

아래와 같은 부분을 찾는다.
    63016                   "Batch Files"
    63017                   "Diff Files"
    63018                   "SQL Query"
    63019                   "Python Script"
    63020                   "Apache Config Files"
    63021                   "PowerShell Script"
END

여기에 아래와 같이 한 줄을 추가한다. 위치는 물론 END 바로 앞이며, 언어를 이미 추가한 경우 적절히 알아서 한다.
    63022                   "Fortran Source Code"
   63023                   "Fortran77 Source Code"


2. Styles.h 수정

아래와 같은 부분을 찾는다.
// Number of Lexers in pLexArray
#define NUMLEXERS 30

NUMLEXERS의 값을 둘 증가시킨다. 아래처럼.


#define NUMLEXERS 32


3. Styles.c 수정 #1

아래와 같은 부분을 찾는다.
// This array holds all the lexers...
PEDITLEXER pLexArray[NUMLEXERS] =
{
  &lexDefault,

바로 앞에 아래와 같은 코드를 추가한다.



// FORTRAN added by BLUEnLIVE - 25th June 2010
KEYWORDLIST KeyWords_FORTRAN = {
"abs achar acos acosd adjustl adjustr aimag aimax0 aimin0 aint ajmax0 "
"ajmin0 akmax0 akmin0 all allocated alog alog10 amax0 amax1 amin0 amin1 "
"amod anint any asin asind associated atan atan2 atan2d atand bitest bitl "
"bitlr bitrl bjtest bit_size bktest break btest cabs ccos cdabs cdcos cdexp "
"cdlog cdsin cdsqrt ceiling cexp char clog cmplx conjg cos cosd cosh count "
"cpu_time cshift csin csqrt dabs dacos dacosd dasin dasind datan datan2 "
"datan2d datand date date_and_time dble dcmplx dconjg dcos dcosd dcosh "
"dcotan ddim dexp dfloat dflotk dfloti dflotj digits dim dimag dint dlog "
"dlog10 dmax1 dmin1 dmod dnint dot_product dprod dreal dsign dsin dsind "
"dsinh dsqrt dtan dtand dtanh eoshift epsilon errsns exp exponent float "
"floati floatj floatk floor fraction free huge iabs iachar iand ibclr ibits "
"ibset ichar idate idim idint idnint ieor ifix iiabs iiand iibclr iibits "
"iibset iidim iidint iidnnt iieor iifix iint iior iiqint iiqnnt iishft "
"iishftc iisign ilen imax0 imax1 imin0 imin1 imod index inint inot int "
"int1 int2 int4 int8 iqint iqnint ior ishft ishftc isign isnan izext jiand "
"jibclr jibits jibset jidim jidint jidnnt jieor jifix jint jior jiqint jiqnnt "
"jishft jishftc jisign jmax0 jmax1 jmin0 jmin1 jmod jnint jnot jzext kiabs "
"kiand kibclr kibits kibset kidim kidint kidnnt kieor kifix kind kint kior "
"kishft kishftc kisign kmax0 kmax1 kmin0 kmin1 kmod knint knot kzext lbound "
"leadz len len_trim lenlge lge lgt lle llt log log10 logical lshift malloc "
"matmul max max0 max1 maxexponent maxloc maxval merge min min0 min1 "
"minexponent minloc minval mod modulo mvbits nearest nint not nworkers "
"number_of_processors pack popcnt poppar precision present product radix "
"random random_number random_seed range real repeat reshape rrspacing rshift "
"scale scan secnds selected_int_kind selected_real_kind set_exponent shape "
"sign sin sind sinh size sizeof sngl snglq spacing spread sqrt sum "
"system_clock tan tand tanh tiny transfer transpose trim ubound unpack verify",
"access action advance allocatable allocate apostrophe assign assignment "
"associate asynchronous backspace bind blank blockdata call case character "
"class close common complex contains continue cycle data deallocate decimal "
"delim default dimension direct do dowhile double doubleprecision else elseif "
"elsewhere encoding end endassociate endblockdata enddo endfile endforall "
"endfunction endif endinterface endmodule endprogram endselect endsubroutine "
"endtype endwhere entry eor equivalence err errmsg exist exit external file "
"flush fmt forall form format formatted function go goto id if implicit in "
"include inout integer inquire intent interface intrinsic iomsg iolength "
"iostat kind len logical module name named namelist nextrec nml none nullify "
"number only open opened operator optional out pad parameter pass pause "
"pending pointer pos position precision print private program protected "
"public quote read readwrite real rec recl recursive result return rewind "
"save select selectcase selecttype sequential sign size stat status stop "
"stream subroutine target then to type unformatted unit use value volatile "
"wait where while write",
"", "", "", "", "", "", "" };

EDITLEXER lexFORTRAN = { SCLEX_FORTRAN, 63022, L"Fortran Source Code", L"f; for; f90; f95", L"", &KeyWords_FORTRAN, {
    { STYLE_DEFAULT, 63126, L"Default", L"", L"" },
    { SCE_F_COMMENT, 63127, L"Comment", L"italics; fore:#008000", L"" },
    { SCE_F_NUMBER, 63130, L"Number", L"bold; fore:#0000FF", L"" },
    { MULTI_STYLE(SCE_F_STRING1,SCE_F_STRING2,SCE_F_STRINGEOL,0), 63131, L"String", L"fore:#008000", L"" },
    { MULTI_STYLE(SCE_F_OPERATOR,SCE_F_OPERATOR,0,0), 63132, L"Operator", L"bold", L"" },
    { SCE_F_IDENTIFIER, 63281, L"Identifier", L"bold; fore:#C80000", L"" },
    { SCE_F_WORD, 63236, L"Word", L"fore:#0000A0; bold", L"" },
    { SCE_F_WORD2, 63260, L"Word2", L"fore:#0000FF; bold", L"" },
    { SCE_F_WORD3, 63203, L"Word3", L"fore:#008000; bold", L"" },
    { SCE_F_PREPROCESSOR, 63133, L"Preprocessor", L"fore:#FF8000", L"" },
    { SCE_F_LABEL, 63235, L"Label", L"fore:#000000; back:#FFFFD1; bold", L"" },
    { SCE_F_CONTINUATION, 63257, L"Continuation", L"back:#FFC0C0", L"" },
    { -1, 00000, L"", L"", L"" } } };

EDITLEXER lexFORTRAN77 = { SCLEX_F77, 63023, L"Fortran77 Source Code", L"f77", L"", &KeyWords_FORTRAN, {
    { STYLE_DEFAULT, 63126, L"Default", L"", L"" },
    { SCE_F_COMMENT, 63127, L"Comment", L"italics; fore:#008000", L"" },
    { SCE_F_NUMBER, 63130, L"Number", L"bold; fore:#0000FF", L"" },
    { MULTI_STYLE(SCE_F_STRING1,SCE_F_STRING2,SCE_F_STRINGEOL,0), 63131, L"String", L"fore:#008000", L"" },
    { MULTI_STYLE(SCE_F_OPERATOR,SCE_F_OPERATOR,0,0), 63132, L"Operator", L"bold", L"" },
    { SCE_F_IDENTIFIER, 63281, L"Identifier", L"bold; fore:#C80000", L"" },
    { SCE_F_WORD, 63236, L"Word", L"fore:#0000A0; bold", L"" },
    { SCE_F_WORD2, 63260, L"Word2", L"fore:#0000FF; bold", L"" },
    { SCE_F_WORD3, 63203, L"Word3", L"fore:#008000; bold", L"" },
    { SCE_F_PREPROCESSOR, 63133, L"Preprocessor", L"fore:#FF8000", L"" },
    { SCE_F_LABEL, 63235, L"Label", L"fore:#000000; back:#FFFFD1; bold", L"" },
    { SCE_F_CONTINUATION, 63257, L"Continuation", L"back:#FFC0C0", L"" },
    { -1, 00000, L"", L"", L"" } } };

여기서 48, 63행의 63022, 63023은 위의 1번에서 추가한 값과 똑같아야 한다.


4. Styles.c 수정 #2


위의 3번에서 찾은 내용은 아래와 같이 생겼다.

PEDITLEXER pLexArray[NUMLEXERS] =
{
  &lexDefault,
  &lexHTML,
  &lexXML,
  &lexCSS,
  &lexJS,
  &lexVBS,

이 배열의 맨 마지막에 아래와 같은 내용을 추가한다.


  &lexFORTRAN,
  &lexFORTRAN77


이러한 수정이 적용된 화면은 아래와 같다.

포트란이 지원되는 메모장2!


2010년 6월 25일 금요일

멀티 코어 CPU에서 코어 별 사용량 측정하는 법 (VS2008)

Visual C++에서 CPU의 사용량을 확인할 때는 PDH(Performance Data Helper)를 이용하는 것이 기본이다.
그런데, 그 방법으로는 멀티코어 CPU에서의 코어 별 사용량을 알 수가 없다.

해결책을 뒤진 결과 Code Project: Getting CPU Usage in a Multiprocessor Machine라는 글을 찾을 수 있었다.
(이 글에 따르면) WMI(Windows Management and Instrumentation)를 이용해야 하며, Win32_PerfRawData_PerfOS_Processor 클래스를 사용하면 원하는 결과를 얻을 수 있다.

위의 글을 보면 작업 순서가 아주 잘 나와있다.
하지만, 그런다고 매번 프로그램을 그 순서에 따라 작성할 수는 없어 간단하게 CCPUUsage라는 클래스를 만들었다.


class CCPUUsage
{
public:
    ...

protected:
    ...

public:
    double *dUsage;
    BOOL GetInitValues();
    void GetValues();

    int inline iNumProc()
    { return iNproc; }

    BOOL inline bOK()
    { return bInitialized; }
};


위의 코드는  클래스 헤더의 일부이며, 이 중 public: 부분만 정리한 것이다.
CCPUUsage 클래스의 인스턴스를 생성한 뒤에 측정을 시작하는 위치에서 GetInitValues()를 호출한 뒤, 측정을 끝낼 위치에서 다시 GetValues()를 호출하면 된다.


{
    CCPUUsage cpuusage;
    BOOL bOK = cpuusage.GetInitValues();
    //작업 시작
    ...
    //작업 종료
    if (bOK)
    {
        cpuusage.GetValues();
        for (int i=0; i<=cpuusage.iNumProc(); i++)
            printf("%g\n", cpuusage.dUsage[i]);
    }
}


결과는 dUsage[코어개수+1]에 저장되는데, [0~코어개수-1] 까지는 각 코어의 사용량이고, [코어개수]엔 평균치가 저장된다.
즉, 쿼드코어의 경우 dUsage[0~4]에 저장되며, dUsage[0~3]은 각 코어의 사용량, dUsage[4]는 평균치가 저장된다.

아래 압축파일을 다운받아 사용하면 되며, 이 코드는 Visual C++ 2008에서 테스트되었다.


덧. 참고한 원래 소스는 Visual C++ 6.0 용이었는데, 실행이 제대로 되지 않았다.
확인해보니 _wtol()가 내가 원하는 대로 동작하지 않은 것이 원인이었다.
결과가 ULONG의 범위를 벗어나면 오버플로우가 발생한 뒤에, 똑같은 값만 나오는 것이다.
그래서, ULONG 대신 __int64를 사용하고, _wtoi64() 사용해버렸다.

2010년 6월 22일 화요일

더 빠른 jpeg 라이브러리 jpeg-turbo 컴파일 삽질기

by BLUEnLIVE | 2010/06/21 07:49

Jpeg 파일을 프로그램에서 읽고 쓸 때 libjpeg 라이브러리를 많이 사용한다. 이 라이브러리는 안정적이고 널리 쓰이고 있지만, 속도가 다소...


VS 2008/2010에서 사용할 수 있는 빠른 jpeg 라이브러리를 찾다가 turbo-jpeg이란 라이브러리를 찾게 되었다.
x86 SIMD ext for IJG JPEG를 개선해서 만든 라이브러리답게, SIMD ext에 비해서 약간 빠르다.

ㅎㄷㄷ한 성능 향상. 아싸!


BUILDING.txt 파일에 컴파일하는 법이 나와있는데, 간략히 간추리면 이렇다.

1. GNU Make v3.7 이상 준비: MSYS나 Cygwin에 들어있음. (그렇다! nmake는 쓰지 않는다!)
2. Windows SDK for Windows Server 2008 and .NET Framework 3.5 준비
3. NASM v0.98 이상 준비: SIMD ext에서와 마찬가지로 어셈블러로 NASM을 사용.
4. make -f win/Makefile

VS 2008과 2010 모두에서 동일한 방법으로 컴파일 가능하며, 성능은 비슷하다.


덧1. 최근 몇 가지 이유로 컴파일러를 VC++ 6.0에서 VS 2008로 이전할 예정인데, 적절한 jpeg 라이브러리를 찾은 듯.
덧2. SIMD ext는 VC++ 6.0에서만 컴파일 가능하고, jpeg-turbo는 VC++ 2008 이상에서만 가능한 듯.
덧3. make는 GNU Make를 쓰지만, link는 VS 2008의 link를 써야 함. path에 지정했다 잠시 삽질 함.

2010년 6월 21일 월요일

빠른 jpeg 라이브러리 x86 SIMD ext for IJG JPEG 컴파일 삽질기

Jpeg 파일을 프로그램에서 읽고 쓸 때 libjpeg 라이브러리를 많이 사용한다.
이 라이브러리는 안정적이고 널리 쓰이고 있지만, 속도가 다소 느린 편이다.

꿀뷰 등의 빠른 이미지 뷰어에서 사용되는 x86 SIMD ext for IJG JPEG 라이브러리가 있다.
이 라이브러리는 libjpeg 6b를 확장해서 일부를 어셈블러로 작성한 것인데, 속도가 상당히 빠르다.
(현재 libjpeg는 8b 까지 공개됨)

이 라이브러리를 컴파일해보고, 이것이 libjpeg 8b에 비해 얼마나 빠른가를 확인해봤다.

컴파일 시에 특별히 준비할 것은 없고, NASM을 사용할 수 있도록 설치하기만 하면 된다.
준비사항은 아래와 같다.

1. NASM 설치
2. 실행파일의 파일명을 nasm.exe에서 nasmw.exe로 변경

Visual Studio 6.0과 Visual Studio 2010에서 같은 코드를 실행시켜 성능을 비교해봤다.


비교는 위 실행화면처럼 연산을 20회 반복한 뒤에 평균시간을 쟀다.
대상은 VC++6에서 컴파일한 6b-SIMD8b 그리고, VC++10에서 컴파일한 8b.
(VC++10에선 6b-SIMD 컴파일 실패했음)


역시 6b-SIMD가 어셈블러로 되어있는지라 상당히 빠르다.

1. 6b-SIMD는 8b에 비해 3배 가까이 빠름
2. 8b도 VC++10에서 컴파일한 것이 10% 정도 빠름
3. VC++10에서 6b-SIMD 컴파일 실패. 성공한 고수님들 계시면 내공 전수 좀…


2010년 6월 20일 일요일

OpenMP로 처음으로 만들어본 멀티코어 프로그램


회사에서 멀티코어 프로그래밍을 해야 될 상황이 도래해서 이리저리 솔루션을 찾아봤다.
그런데, 인텔에서 판매하는 수학 라이브러리 등과는 달리, OpenMP라는 솔루션은 이미 VC++에 내장되어 있었다!
(인텔 Math Library는 멀티코어를 지원하며, 성능이 짱이라고 함. 그런다고 회사에서 사줄리는…)

관련 문서를 좀 읽어본 뒤에 처음으로 멀티코어 프로그래밍에 도전했다.

대상은 이미지 리샘플링.
기존에 만들어 둔 Lanczos3 리샘플을 멀티코어 버전으로 수정했더니, 성능 향상이 눈에 확 띤다.

내 Quad-core에서 (당연히) 쓰레드를 4개로 돌릴 때가 최강의 성능을 보임


역시 예상했던 대로 쓰레드의 수를 5개(코어의 갯수) 이상으로 하는 것은 아무런 의미가 없다.

일단 뭔가 한 발짝을 내딛은 것 같아 뿌듯함.

덧. 이와 함께 jpeglib 6b의 SIMD 확장 버전을 VC++ 2008/2010에서 컴파일해보다 실패했다.
VC++ 6에서는 완벽하게 컴파일되는데, 뭐가 문제일까?
혹시 고수님 계시면 도움 좀 부탁드립니다. (굽신굽신)

2010년 5월 22일 토요일

SRT 자막 보정 프로그램

자막 파일로 우리나라에서는 대부분 SMI 포맷을 사용하지만, 외국어 자막들은 SRT 포맷이 많이 사용된다.
MP4, MKV등의 동영상 파일에 손쉽게 삽입할 수 있다는 장점도 있지만, 무엇보다 DVD의 자막을 추출하는 SubRip에서 기본적으로 지원하는 포맷이기 때문이다.

문제는 SubRip에서 자막을 읽어들이는 것이 완벽하지 않다는 것.

DVD의 자막은 글자가 아니라 (자막이라는) 그림이 저장되고, SubRip는 이 자막을 OCR로 읽어들인다.
이 과정에서 l(L의 소문자)과 I(i의 대문자)를 제대로 구분하지 못하거나, 숫자 사이에 공백이 추가되는 등의 문제가 생긴다.

사실, SubRip에서 OCR을 마친 뒤에 기본적인 맞춤법 교정을 할 수 있는데, 많은 사용자들이 이 작업을 패스하고 있다.

이러한 문제들을 어느 정도 보정해주는 프로그램을 만들었다.

영문자막의 수정에 관심이 있는 사용자에게 작으나마 도움이 될 것이다.

화면은 대략 아래와 같고...


아래 링크에서 다운받을 수 있다.


2010년 4월 16일 금요일

Nokia 인증 도구 도우미

노키아 익뮤(5800)나 6210s을 쓰는 사용자 중 많은 분들이 인증도구를 사용한다.

ken_zhang이란 분이 만든 이 인증도구(정확히는 Signsis.exe의 GUI)는 기능은 차치하고, 인터페이스가 불편하다.
무엇보다 매번 실행할 때마다 signsis.exe와 *.cer, *.key 파일을 일일이 지정해야 된다는 거...


사실, 이 프로그램은 Nokia 사에서 만든 Signsis.exe의 GUI에 해당하는 것으로, 기능 자체는 특별할 것이 없다.
단지, 좀 불편한 파라미터 입력을 단순화시킨 것일 뿐... (원래 GUI들이 다 그런 거지만)

그래서 좀 더 편리한 인터페이스의 GUI를 만들어봤다.

이 프로그램의 가장 큰 장점들은 아래와 같다.

1. 실행파일(NokisSignTool.exe)과 같은 폴더에 signsis.exe가 있으면 자동으로 등록
2. 실행파일과 같은 폴더에 *.cert, *.key가 있으면 그 중 가장 먼저 발견된 것을 자동으로 등록
3. 파일명을 FileDialog로 입력받지 않고, 드래그 앤 드롭으로 지정
4. 원본 SIS 파일만 지정하면 타겟 SIS 파일명은 자동 지정

즉, 사용자 입력을 최소화했다는 것이다.


프로그램은 아래 링크에서 다운받을 수 있다.


덧. 노키아 스마트 폰에서의 인증 개념 등등은 네이버 카페를 참고하기 바란다.