28package com.jogamp.graph.ui.shapes;
30import com.jogamp.opengl.GL;
31import com.jogamp.opengl.GL2ES2;
32import com.jogamp.opengl.GLProfile;
34import java.util.ArrayList;
37import com.jogamp.common.av.AudioSink;
38import com.jogamp.common.util.InterruptSource;
39import com.jogamp.common.util.StringUtil;
40import com.jogamp.graph.curve.Region;
41import com.jogamp.graph.curve.opengl.RegionRenderer;
42import com.jogamp.graph.font.Font;
43import com.jogamp.graph.font.FontFactory;
44import com.jogamp.graph.ui.GraphShape;
45import com.jogamp.graph.ui.layout.Alignment;
46import com.jogamp.math.Vec2f;
47import com.jogamp.math.Vec4f;
48import com.jogamp.math.geom.AABBox;
49import com.jogamp.math.util.PMVMatrix4f;
50import com.jogamp.opengl.util.av.SubTextEvent;
51import com.jogamp.opengl.util.av.SubBitmapEvent;
52import com.jogamp.opengl.util.av.SubtitleEvent;
53import com.jogamp.opengl.util.av.SubtitleEventListener;
54import com.jogamp.opengl.util.texture.ImageSequence;
55import com.jogamp.opengl.util.av.CodecID;
56import com.jogamp.opengl.util.av.GLMediaPlayer;
57import com.jogamp.opengl.util.av.GLMediaPlayer.GLMediaEventListener;
58import com.jogamp.opengl.util.av.GLMediaPlayer.StreamException;
78 private static final boolean DEBUG_SUB =
false;
79 private static final boolean DEBUG_SUB_LAYOUT =
false;
80 private boolean verbose =
false;
83 private Font subFallbackFont;
84 private final Label subLabel;
85 private final float subZOffset;
86 private boolean subEnabled;
87 private float subLineHeightPct;
88 private float subLineDY;
104 private static final float ASS_SUB_USED_WIDTH = 0.90f;
105 private static final float ASS_SUB_BLEND_ADDED_HEIGHT = 0.25f;
106 private static final int ASS_SUB_MAX_SPLIT_LINES = 4;
111 private final List<SubtitleEvent> subEventQueue =
new ArrayList<SubtitleEvent>();
112 private final Object subEventLock =
new Object();
144 this.subLabel.
moveTo(0, 0, subZOffset);
153 this.drawLastSub =
null;
170 if(
null != subFont ) {
171 this.subFont = subFont;
176 this.subLineHeightPct = subLineHeightPct;
177 this.subLineDY = subLineDY;
178 this.subAlignment = subAlignment;
179 this.subEnabled =
true;
188 this.subBlend.
setColor( 0, 0, 0, blend );
195 synchronized( subEventLock ) {
196 subEventQueue.add(e);
198 System.err.println(
"MediaButton: GOT #"+subEventQueue.size()+
": "+e);
222 System.err.println(
"MediaButton AttributesChanges: "+eventMask+
", when "+when);
223 System.err.println(
"MediaButton State: "+mp);
225 if( eventMask.isSet(GLMediaPlayer.EventMask.Bit.Init) ) {
226 clearSubtitleCache();
228 }
else if( eventMask.isSet(GLMediaPlayer.EventMask.Bit.Uninit) ||
229 eventMask.isSet(GLMediaPlayer.EventMask.Bit.Play) ||
230 eventMask.isSet(GLMediaPlayer.EventMask.Bit.Seek) ||
231 eventMask.isSet(GLMediaPlayer.EventMask.Bit.SID) ) {
232 clearSubtitleCache();
234 }
else if( eventMask.isSet(GLMediaPlayer.EventMask.Bit.Pause) ) {
235 clearSubtitleCacheButLast();
238 if( eventMask.isSet(GLMediaPlayer.EventMask.Bit.Size) ) {
241 if( eventMask.isSet(GLMediaPlayer.EventMask.Bit.EOS) ) {
242 new InterruptSource.Thread() {
249 }
else if( eventMask.isSet(GLMediaPlayer.EventMask.Bit.Error) ) {
252 se.printStackTrace();
262 clearSubtitleCache();
267 clearSubtitleCache();
268 subTexImg.
destroy(gl, renderer);
269 subLabel.
destroy(gl, renderer);
270 subBlend.
destroy(gl, renderer);
274 volatile boolean resetGL =
true;
278 super.addShapeToRegion(glp, gl);
287 clearSubtitleCache();
292 }
catch (
final Exception e) {
296 super.drawImpl0(gl, renderer, rgba);
298 drawSubtitle(gl, renderer);
304 private final void clearSubtitleCache() {
305 synchronized( subEventLock ) {
308 if(
null != lastSub ) {
312 for(
final SubtitleEvent e : subEventQueue) {
315 subEventQueue.clear();
317 System.err.println(
"MediaButton.clearSubtitleCache: "+subEventQueue.size()+
", last "+lastSub);
321 private final void clearSubtitleCacheButLast() {
322 synchronized( subEventLock ) {
323 final SubtitleEvent lastSub = drawLastSub;
324 for(
int i=subEventQueue.size()-1; i>=0; --i) {
325 final SubtitleEvent e = subEventQueue.get(i);
328 subEventQueue.remove(i);
332 System.err.println(
"MediaButton.clearSubtitleCacheButLast: "+subEventQueue.size()+
", last "+lastSub);
336 private final void drawSubtitle(
final GL2ES2 gl,
final RegionRenderer renderer) {
337 final GLMediaPlayer mPlayer = (GLMediaPlayer)
texSeq;
338 final int pts = mPlayer.getPTS().getCurrent();
341 SubtitleEvent lastSub = drawLastSub;
343 if(
null != lastSub && lastSub.pts_end < pts ) {
345 System.err.println(
"MediaButton: Drop.0: pts "+pts+
", "+lastSub);
355 final SubtitleEvent sub;
356 final boolean newSub;
358 final SubtitleEvent gotSub;
359 synchronized( subEventLock ) {
360 if( subEventQueue.size() > 0 ) {
361 final SubtitleEvent e = subEventQueue.get(0);
362 if( e.pts_start <= pts && pts <= e.pts_end ) {
364 subEventQueue.remove(0);
365 }
else if( e.pts_end < pts ) {
367 subEventQueue.remove(0);
370 System.err.println(
"MediaButton: Drop.1: pts "+pts+
", "+e);
380 if(
null == gotSub ) {
384 if(
null != lastSub ) {
388 if( SubtitleEvent.Type.Empty == gotSub.type ) {
393 System.err.println(
"MediaButton: Empty: pts "+pts+
", "+gotSub);
396 drawLastSub = gotSub;
405 }
else if( SubtitleEvent.Type.Text == sub.type ) {
406 final SubTextEvent assSub = (SubTextEvent)sub;
408 final float maxWidth =
box.
getWidth() * ASS_SUB_USED_WIDTH;
409 subLabel.
setFont( Font.getBestCoverage(subFont, subFallbackFont, assSub.text) );
411 int lines = assSub.lines;
412 AABBox subBox = subLabel.
getBounds(gl.getGLProfile());
413 float subLineHeight = subBox.
getHeight() / lines;
415 float s_s = lineHeight / subLineHeight;
416 if( s_s * subBox.getWidth() > maxWidth ) {
419 if( DEBUG_SUB_LAYOUT ) {
420 System.err.println(
"XXX split.0 has lines "+lines+
", s "+s_s+
", width "+(s_s * subBox.getWidth())+
" > "+maxWidth+
": "+assSub.text);
422 final String trimmed = StringUtil.trim(assSub.text, StringUtil.WHITESPACE,
" ");
423 lines = Math.min(ASS_SUB_MAX_SPLIT_LINES, (
int)Math.ceil( s_s * subBox.getWidth() / maxWidth ));
424 final String text = StringUtil.split(trimmed, lines,
" ", String.valueOf(StringUtil.LF));
425 lines = StringUtil.getLineCount(text);
427 subBox = subLabel.
getBounds(gl.getGLProfile());
428 subLineHeight = subBox.
getHeight() / lines;
430 s_s = lineHeight / subLineHeight;
431 if( DEBUG_SUB_LAYOUT ) {
432 System.err.println(
"XXX split.X to lines "+lines+
", s "+s_s+
", width "+(s_s * subBox.getWidth())+
" <=?= "+maxWidth+
": "+text);
434 if( s_s * subBox.getWidth() > maxWidth ) {
436 s_s = maxWidth / subBox.getWidth();
437 lineHeight *= s_s / ( lineHeight / subLineHeight );
438 if( DEBUG_SUB_LAYOUT ) {
439 System.err.println(
"XXX scale-down scale "+s_s+
", width "+(s_s * subBox.getWidth())+
" <= "+maxWidth+
": "+text);
445 final float labelHeight = lineHeight * lines;
446 final float blendHeight = labelHeight + lineHeight * ASS_SUB_BLEND_ADDED_HEIGHT;
447 final Vec2f v_sz =
new Vec2f(mPlayer.getWidth(), mPlayer.getHeight());
449 final float v_s = Math.min( v_sxy.x(), v_sxy.y() );
450 final Vec2f v_ctr =
new Vec2f(v_sz).scale(0.5f);
451 final Vec2f b_ctr =
new Vec2f(
box.
getCenter()).scale(1/v_s);
452 final float d_bl = ( blendHeight - labelHeight ) * 0.5f;
453 final float v_maxWidth = v_sz.x() * ASS_SUB_USED_WIDTH;
454 final float d_vmw = v_sz.x() - v_maxWidth;
456 if( subAlignment.
isSet( Alignment.Bit.Left) ) {
458 final Vec2f s_p0 =
new Vec2f( d_vmw*0.5f,
459 ( subLineHeight * subLineDY * s_s ) / v_s);
460 s_p0_s = s_p0.sub( v_ctr ).add(b_ctr).scale( v_s ).add(0, d_bl);
463 final Vec2f s_p0 =
new Vec2f( d_vmw*0.5f + ( v_maxWidth - subBox.getWidth()*s_s/v_s )*0.5f,
464 ( subLineHeight * subLineDY * s_s ) / v_s);
465 s_p0_s = s_p0.sub( v_ctr ).add(b_ctr).scale( v_s ).add(0, d_bl);
467 subLabel.
moveTo(s_p0_s.x(), s_p0_s.y(), 2*subZOffset);
470 subBlend.
setPosition(0, s_p0_s.y() - d_bl, 1*subZOffset);
472 System.err.println(
"MediaButton: NEXT pts "+pts+
", "+sub);
475 subBlend.
draw(gl, renderer);
476 final PMVMatrix4f pmv = renderer.getMatrix();
479 subLabel.
draw(gl, renderer);
481 }
else if( SubtitleEvent.Type.Bitmap == sub.type ) {
482 final SubBitmapEvent texSub = (SubBitmapEvent)sub;
485 System.err.println(
"MediaButton: NEXT pts "+pts+
", "+sub);
487 if(
null != texSub.texture ) {
490 imgSeq.addFrame(gl, texSub.texture);
491 final Vec2f v_sz =
new Vec2f(mPlayer.getWidth(), mPlayer.getHeight());
493 final float v_s = Math.min(v_sxy.x(), v_sxy.y());
494 final Vec2f s_sz_s =
new Vec2f(texSub.dimension).scale(v_s);
495 subTexImg.
setSize(s_sz_s.x(), s_sz_s.y());
498 if( CodecID.HDMV_PGS == sub.codec && mPlayer.getWidth() < 1920 && mPlayer.getHeight() == 1080 ) {
501 v_ctr =
new Vec2f(
new Vec2f(1920, 1080)).
scale(0.5f);
503 v_ctr =
new Vec2f(v_sz).scale(0.5f);
506 final Vec2f s_p0 =
new Vec2f(texSub.position.x(),
507 v_sz.y() - texSub.position.y() - texSub.dimension.y() );
508 final Vec2f s_p0_s = s_p0.minus( v_ctr ).add( b_ctr ).scale( v_s );
509 subTexImg.
moveTo(s_p0_s.x(), s_p0_s.y(), 2*subZOffset);
511 if( DEBUG_SUB_LAYOUT ) {
514 final float v_ar = v_sz.x()/v_sz.y();
515 final float b_ar = b_sz.x()/b_sz.y();
516 final float s_ar = s_sz_s.x()/s_sz_s.y();
517 final float s_x_centered = ( b_sz.x() - s_sz_s.x() ) * 0.5f;
518 final Vec2f v_ctr_1080p =
new Vec2f(
new Vec2f(1920, 1080)).scale(0.5f);
519 final Vec2f v_ctr_o =
new Vec2f(v_sz).scale(0.5f);
520 final Vec2f s_sz =
new Vec2f(texSub.dimension);
522 final Vec2f v_p0_ctr = s_p0.minus(v_ctr);
523 final Vec2f s_p1 = b_ctr.plus(v_p0_ctr);
524 System.err.println(
"XX video "+v_sz+
" (ar "+v_ar+
"), ( v_ctr "+v_ctr_o+
", v_ctr_1080p "+v_ctr_1080p+
" ) -> v_ctr "+v_ctr);
525 System.err.println(
"XX sub s_sz "+s_sz+
", s_sz_s "+s_sz_s+
" (ar "+s_ar+
")");
526 System.err.println(
"XX box "+b_sz+
" (ar "+b_ar+
"), b_ctr "+b_ctr+
", b_ctr_s "+b_ctr_s);
527 System.err.println(
"XXX v_s "+v_sxy+
" -> "+v_s+
": sz "+s_sz_s);
528 System.err.println(
"XXX p0 "+s_p0+
", v_p0_ctr "+v_p0_ctr+
", s_p1 "+s_p1+
" -> s_p1_s "+s_p0_s+
"; sxs_2 "+s_x_centered);
532 final PMVMatrix4f pmv = renderer.getMatrix();
535 subTexImg.
draw(gl, renderer);
Abstract Outline shape representation define the method an OutlineShape(s) is bound and rendered.
static final int AA_RENDERING_MASK
2-pass rendering bit-mask including MSAA_RENDERING_BIT and VBAA_RENDERING_BIT.
final void markShapeDirty()
Mark this region's shape dirty, i.e.
The optional property jogamp.graph.font.ctor allows user to specify the FontConstructor implementatio...
static synchronized Font getFallbackFont()
Returns registered fallback Font, maybe null.
static Font getDefaultFont()
Returns default Font of default FontSet or null if n/a.
Shape setColor(final float r, final float g, final float b, final float a)
Set base color.
final Shape setScale(final Vec3f s)
Set scale factor to given scale.
final Shape setInteractive(final boolean v)
Set whether this shape is interactive in general, i.e.
void draw(final GL2ES2 gl, final RegionRenderer renderer)
Renders the shape.
final void markStateDirty()
Marks the rendering state dirty, causing next draw() to notify the Graph region to reselect shader an...
final Shape moveTo(final float tx, final float ty, final float tz)
Move to scaled position.
final AABBox getBounds()
Returns the unscaled bounding AABBox for this shape, borrowing internal instance.
Shape setPressedColorMod(final float r, final float g, final float b, final float a)
Set pressed color, modulating getColor() if isPressed().
final Shape setDragAndResizable(final boolean v)
Set whether this shape is draggable and resizable.
final Shape setToggleOnColorMod(final float r, final float g, final float b, final float a)
Set toggle-on color, modulating getColor() if isToggleOn() and setToggleable(boolean).
final Shape setToggleOffColorMod(final float r, final float g, final float b, final float a)
Set toggle-off color, modulating getColor() if !isToggleOn() and setToggleable(boolean).
final void destroy(final GL2ES2 gl, final RegionRenderer renderer)
Destroys all data.
final void applyMatToMv(final PMVMatrix4f pmv)
Applies the internal Matrix4f to the given modelview matrix, i.e.
final Shape scale(final Vec3f s)
Multiply current scale factor by given scale.
final Shape setToggleable(final boolean toggleable)
Set this shape toggleable, default is off.
Immutable layout alignment options, including Bit#Fill.
static final Alignment CenterHoriz
Bit#CenterHoriz alignment constant.
boolean isSet(final Bit bit)
A GraphUI text label GraphShape.
boolean setText(final CharSequence text)
Set the text to be rendered.
boolean setFont(final Font font)
Set the Font used to render the text.
A GraphUI rectangle GraphShape.
void setDimension(final float width, final float height, final float lineWidth)
void setPosition(final float minX, final float minY, final float zPos)
Vec3f scale(final float s)
this = this * s, returns this.
4D Vector based upon four float components.
final Vec3f getCenter()
Returns computed center of this AABBox of getLow() and getHigh().
Specifies the the OpenGL profile.
abstract void release()
Release the resources, if any, back to the owner.
Simple TextureSequence implementation allowing existing textures or image streams to be used and repl...
void setParams(final int magFilter, final int minFilter, final int wrapS, final int wrapT)
Interface wrapper for font implementation.
static final int GL_LINEAR
GL_ES_VERSION_2_0, GL_VERSION_1_1, GL_VERSION_1_0, GL_VERSION_ES_1_0 Define "GL_LINEAR" with expressi...
static final int GL_CLAMP_TO_EDGE
GL_ES_VERSION_2_0, GL_VERSION_1_2, GL_VERSION_ES_1_0, GL_SGIS_texture_edge_clamp Alias for: GL_CLAMP_...
int getTextureUnit()
Return the texture unit used to render the current frame.