ScreenshotMaker: 캡춰, 수정, 그리고 화면을 Flicker에 업로드 하거나 디스크에 저장하기

By Jean-Francois Denise, October 6, 2008

JavaFX 테크놀로지를 가지고 여러분 자신만의 "picture retouching"을 만들고 안정적인 웹 서비스와 연결하는 것은 쉽습니다. 이 샘플 어플리케이션은 여러분의 화면을 캡춰하고 화살표들과 텍스트를 추가함으로써 스크린샷을 수정하고 마지막으로 이것을 디스크로 저장하거나 Flickr에 업로드 하는것을 가능하게 해줍니다. 전체 이미지나 그 일부는 업로드 될수 있습니다.

이 샘플은 Desktop platform에서만 작동합니다.

Run Example with Java Webstart

코드 이해하기

이 샘플은 6개의 파일들로 구성되어 있습니다:

  • ScreenshotMaker.fx: 이 샘플의 메인 소스입니다. 화면을 캡춰할 "one button frame"과 캡춰된 화면 그림을 손질할 프레임을 만듭니다.
  • Palette.fx: 이미지 수정을 가능하게 하는 버튼들을 포함하는 팔레트.
  • FlickrPhotoUpload.fx: javafx.io.HttpRequest를 확장하고 Flicker 사진 업로드를 다루는 클래스.
  • Flickr.java: Filckr 컨텐츠 생성을 다루는 클래스. 대게는 signing과 HTTP message 작성을 요청합니다.
  • ScreenCapturer.java: 스크린샷 캡춰를 포착하는 간단한 클래스
  • Util.java: 몇몇 유틸리티 기능들을 제공하는 간단한 클래스.

이미지 손질하기

팔레트

상단 오른쪽 구석의 팔레트는 여러분이 이미지에 적용할 툴을 선택할수 있게해줍니다. 버튼들은 Group에 그룹지어진 기본 Java FX 모양들(선, 사각형, 다각형, 폴리라인-선분을 이어만든 다각형) 조합의 결과입니다. 여러분이 원하는 모양들을 정확하게 위치시키는 것은 매우 간단합니다. 여러분은 각 점들의 좌표들만 제공되길 바라면 됩니다. Palette.fx는 레이아웃을 포함합니다.

팔레트의 실제 크기는 보여지는 팔레트보다 더 큽니다. 팔레트를 실제 크기로 확장하는 것은 여러분이 매우 상세한 아이콘을 디자인할수 있게 해줍니다. Stage에 팔레트가 추가되었을때 scaling은 화면에 맞게 조정됩니다.

버튼에 마우스 포인터가 들어가거나 벗어날때 마다 효과는 버튼에 적용됩니다.

Source Code
        button.effect = DropShadow{ offsetX: 0 offsetY: 0 color: Color.WHITE radius: 30 };
    

버튼이 선택되는 될때마다 선택을 가능하게하기 위해서 shadow는 적용됩니다. Palette.ButtonFeedback는 선택 로직을 구현합니다.

툴팁들은 (Palette.Tooltip 클래스의 인스턴스들) 각 버튼들을 위해서 제공됩니다. 툴팁 delay는 Timeline를 사용하여 구현됩니다.

Source Code
    timeline = Timeline {
        repeatCount: Timeline.INDEFINITE
        keyFrames :
            KeyFrame {
                time : 1000ms
                action: function() {
                    currentToolTip.visible = true;
                }
            }
        };

마우스 포인터가 버튼을 벗어날때 timeline은 멈춰지고 tooltip은 숨겨집니다.

이미지를 Frame안으로 Drag하기

스크린샷을 캡춰할때 문제는 그것이 여러분의 실제크기보다 더 크다는 것입니다. 그래서, 여러분은 어떻게 캡춰된 이미지, 이 이미지를 수정할 툴바, 그리고 윈도우 decoration을 포함한 윈도우를 표시할수 있습니까? 여러분은 실제로 이미지를 리사이즈 하지 않고는 할수 없습니다. 아마 이 세가지의 컴포넌트들을 표시하는것은 여러분이 애초에 원했던 것이 아닙니다. 저것이 이미지 에디터가 윈도우에 있는 이미지의 일부분을 보여주는 이유입니다. 그것은 원래 화면의 85 퍼센트를 보여줍니다. 이미지의 몇몇 부분들은 그리고 나서 숨겨집니다. 여러분은 드래그 이미지 버튼을 클릭함으로써 이미지를 드래그 할수 있습니다. 클릭을 하고 이미지를 드래그 해보세요. 그러면 숨겨진 부분이 표시됩니다. 여러분이 보여지는 부분에 만족할때 마우스 버튼을 릴리즈 하세요. 술적으로, 여러분은 ImageView를 translate하고 그것의 viewPort(화면상의 표시영역)을 줄이거나 크게함으로써 이러한 일련의 행동(action)들을 얻을수 있습니다.

Source Code
        imgView = ImageView{
            // When the mouse is dragged, the viewPort is resized in order to
            // display the image zone that enters the visible zone.
            viewport :bind translatedRect
            // Translates the image in the visible / non visible zone.
            transforms:Translate {x: bind movedX y : bind movedY}
            image : Image {
                url: imgURL
            }
        }
    

마우스가 드래그 되었을때 새로운 ImageView 좌표와 viewPort 크기는 재계산 됩니다. 증가되거나 감소분은 ImageView에 대입됩니다. 최대 숨겨진 구역은 숨겨진 부분이 더이상 보여질 것이 없을때 translation을 멈추는데 사용되곤합니다.

Scene에 Node 인스턴스 동적으로 추가하기

화살표와 텍스트는Scene에 위치한 Group에 추가됩니다. 그룹(groupRef)의 로컬 레퍼런스(local reference)는 유지됩니다. 화살표나 텍스트가 생성될때마다 그것은 그룹 컨텐츠에 추가됩니다:

Source Code
        insert currentArrow into groupRef.content;
    

Arrow 추가하기

Add Arrow 버튼을 클릭해보세요. 여러분의 마우스 포인터를 화살표가 시작했으면 하는 곳으로 옮겨서 누르고 드래그 하세요. 화살표가 그려집니다. Arrow는 주요 Line와 화살촉 형태인 두개의 작은 Line 인스턴스 그룹인 Java FX CustomNode 입니다. 머리는 마우스가 그래그한 방향을 따라거 회전합니다. 축은 line endX 입니다.

Source Code
         Line {
            transforms: Rotate{angle: angle pivotX:line.endX pivotY:line.endY }
            ...
    

Click an Arrow instance and drag it to change its location.

Arrow 인스턴스를 클릭하고 위치를 변경하기 위해서 드래그하세요.

Text 추가하기

Add Text 버튼을 클릭하세요. 여러분의 마우스 포인터를 여러분이 문자 타이핑하기를 원하는 곳으로 이동하세요. 텍스트는 CustomNode를 상속받는 EditabelText 입니다. 그것은 편집하고 타이핑된 텍스트를 표시하기 위한 javafx.scene.control.TextBox로 구성되었습니다. 여러분이 텍스트를 더블클릭할때 여러분은 편집 모드로 되돌아갑니다. 텍스트를 표시 모드로 변경하기 위해서 리턴키를 입력하거나 다른 곳을 클릭하세요. EditableText 인스턴스를 클릭하고 위치 변경을 위해서 드래그 하세요.

활성화가 되기위해서 여러분은 새롭게 생성된TextBox에 포커스를 줘야합니다. 여러분은 이러한 변화를 호출로 만들수 있습니다:

Source Code
    tb.requestFocus();
    

이미지의 부분 선택하기

Select Region 버튼을 클릭하세요. 여러분의 마우스 포인터를 여러분이 선택하기를 원하는 구역의 최상단 왼쪽으로 이동하세요. 여러분이 누르고 드래그 할때 연두색 사각형이 그려집니다. 사각형의 최상단에 현재 부분의 너비와 높이가 표시됩니다. 사각형은 Flickr에 업로드될 구역을 cover합니다.

SelectRegionRectangle 클래스는 가로와 너비 레이블을 시각화하는데 도움을 주는 주 Rectangle, Text와 작은 Rectangle 로 조합된 CustomNode입니다.

Selection Rectangle를 개발하는데 트릭적인한 부분은 Rectangle의 절대좌표(화면 이내의)를 검색하는것입니다. 그렇게 하기 위해서 여러분은 화면에 절대적인 Stage 좌표, 윈도우 데코레이션을 제거하기 위해서 Stage.scene좌표, 마지막으로 Rectangle 좌표를 얻기 원할것입니다. 이 작업은 selectionRectangle 메소드를 사용해서 실행됩니다.

NB:만약 어느 부분도 선택되지 않았아면 보여지는 이미지는 업로드 되거나 저장됩니다.

이미지를 디스크에 저장하기

이미지는 Save To Disk 버튼을 클릭함으로서 저장됩니다. 여러분이 이미지를 저장하기위한 파일을 제공할수 있게 해주는 javax.swing.JFileChooser가 표시됩니다. 만약 파일이 이미 존재한다면 그 내용은 덮어씌워집니다. 이미지가 저장된 후 메인 윈도우는 닫혀집니다.

Flickr에 이미지 업로드하기

Flickr에 이미지 업로드가 가능하게하기 위해서 여러분은 유효한 Flicker API 키, Shared Secret, Authentication Token이 필요합니다. Flickr 웹 서비스를 좀더 이해하기 위해서 여러분은 API 사용법에 있는 Flickr online documentation를 읽어야만 합니다.

여러분은 팝업 윈도우(여러분이 flickr Upload를 클릭한후 표시되는)에서 이 credentials들을 제공할수 있습니다. 만약 여러분이 이 credential들을 유지하고 싶다면 여러분은 아래 정의들(ScreenshotMaker.fx 파일에 위치한)을 갱신할수 있습니다:

Source Code
        var SECRET:String = "<Your Shared Secret>";
        var API_KEY:String = "<Your API Key>";
        var AUTH_TOKEN:String = "<Your Authentication Token>";
    

여러분이 flickr Upload를 눌렀을때 태그와 credential들을 제공할수 있게하는 윈도우가 표시됩니다. Upload 버튼을 누르고 이미지는 여러분의 계정에 업로드됩니다. 업로드가 끝나면 주 윈도우는 닫혀집니다. 업로드 하는 동안 실패가 발생하는 경우 에러 메세지는 Flickr Upload 버튼 아래에 표시되고 메인 윈도우는 닫혀지지 않습니다.

Java FX는 RestFul 웹 서비스를 다루기 위한 새로운 API를 제공합니다. 이 API는 javafx.io.http 패키지에 위치해 있습니다. 이 이벤트-드리븐 API는 여러분이 여러분의 HTTP request들에 여러분의 graphical 컴포넌트들을 연결할수 있도록 해줍니다. 이 경우 FlickrPhotoUpload 클래스는 Figure 1에서 보여지는 것처럼 request전송과 success status로 받은 response를 translate를 다루도록 정의 되어 있었습니다. HttpRequest 클래스가 제공하는 모든것은 여러분이 UI와 함께 HTTP response 상태를 구성하기 위해서 사용할수 있는 치환 트리거(replace trigger) 위에 있는 done 입니다.

Source Code
        // Construct an instance in charge to send the upload request.
        // User inputs are provided.
        var req = FlickrPhotoUpload {
                api_key:api_key.text
                secret:api_secret.text
                auth_token:auth_token.text
                tags:tags.text
                file:file
                type:"jpg"
                // Link the response with the status of the graphical elements.
                override var done on replace {
                    if(done) {
                        enable();
                        if(not success) {
                            status = "Upload failed";
                        } else {
                            widgetFrame.visible = true;
                            f.visible = false;
                        }
                    }
                }
            }
        // Enqueue the request to process it.
        req.enqueue();
    

Figure 1: Flickr Upload Request 보내기

Tip:request HTTP 헤더가 위치할 최상의 장소는 the enqueue 메소드 입니다. 여러분은 enqueue를 재정의할 하위 클래스가 필요합니다. 예를 들어 Figure 2를 보세요.

Source Code
        override function enqueue(): Integer {
            content = Flickr.computePhotoUploadContent(api_key,
                secret,
                auth_token,
                tags,
                file,
                type);
            setHeader("Content-Type", content.contentType);
            setHeader("Content-Length", String.valueOf(content.content.length));
            super.enqueue();
        }
    

Figure 2: Request HTTP 헤더 세팅하기

java.lang.FX.deferAction로 Event Order 제어하기

이 샘플은 이벤트들이 정돈되는(ordered) 경우들을 강조합니다. 녹색 선택 사각형은 사각형이 캡춰되지 전에 숨겨져야만 합니다. 까만 "waiting" 사각형은 사각형이 캡춰된 이후 표시되어야만 합니다. Flickr 업로드 처리는 메인 Java FX UI 쓰레드 밖에서 작동되어야만 합니다. java.lang.FX.deferAction 메소드는 여러분이 현재 이벤트가 종료되어진 후에 태스크를 실행할수 있도록 해줍니다. 그것은 SwingUtilities.invokeLater를 호출하는 것과 유사합니다.

Source Code
        // Capture the screenshot and display the Retouch window after the current
        // task is terminated.
        FX.deferAction(function() {
            captureAndRetouch();
            });
        }
    

새로운 Screenshot 캡춰하기

메인 윈도우를 닫고 여러분의 화면 하단에 "single-button" 윈도우가 다시 나타나도록 하기위해서 Back to Capture 버튼을 클릭하세요. 메인 윈도우를 닫는 것은 이 "single-button" 윈도우가 표시되는 것을 확실하게 합니다.